File obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032.obscpio of Package obs-studio
07070100000000000081A400000000000000000000000168D5836B0000172B000000000000000000000000000000000000004300000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/.clang-format# please use clang-format version 16 or later
Standard: c++17
AccessModifierOffset: -8
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Left
AlignOperands: true
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: false
AllowAllConstructorInitializersOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: false
AllowShortLambdasOnASingleLine: Inline
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: false
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: true
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Custom
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon
BreakStringLiterals: false # apparently unpredictable
ColumnLimit: 120
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 8
ContinuationIndentWidth: 8
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
FixNamespaceComments: true
ForEachMacros:
- 'json_object_foreach'
- 'json_object_foreach_safe'
- 'json_array_foreach'
- 'HASH_ITER'
IncludeBlocks: Preserve
IndentCaseLabels: false
IndentPPDirectives: None
IndentWidth: 8
IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks: true
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 8
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 10
PenaltyBreakBeforeFirstCallParameter: 30
PenaltyBreakComment: 10
PenaltyBreakFirstLessLess: 0
PenaltyBreakString: 10
PenaltyExcessCharacter: 100
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
ReflowComments: false
SkipMacroDefinitionBody: true
SortIncludes: false
SortUsingDeclarations: false
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
SpacesInContainerLiterals: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
StatementMacros:
- 'Q_OBJECT'
TabWidth: 8
TypenameMacros:
- 'DARRAY'
UseTab: ForContinuationAndIndentation
---
Language: ObjC
AccessModifierOffset: 2
AlignArrayOfStructures: Right
AlignConsecutiveAssignments: None
AlignConsecutiveBitFields: None
AlignConsecutiveDeclarations: None
AlignConsecutiveMacros:
Enabled: true
AcrossEmptyLines: false
AcrossComments: true
AllowShortBlocksOnASingleLine: Never
AllowShortEnumsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: Never
AllowShortLambdasOnASingleLine: None
AttributeMacros: ['__unused', '__autoreleasing', '_Nonnull', '__bridge']
BitFieldColonSpacing: Both
#BreakBeforeBraces: Webkit
BreakBeforeBraces: Custom
BraceWrapping:
AfterCaseLabel: false
AfterClass: true
AfterControlStatement: Never
AfterEnum: false
AfterFunction: true
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: false
SplitEmptyRecord: false
SplitEmptyNamespace: true
BreakAfterAttributes: Never
BreakArrays: false
BreakBeforeConceptDeclarations: Allowed
BreakBeforeInlineASMColon: OnlyMultiline
BreakConstructorInitializers: AfterColon
BreakInheritanceList: AfterComma
ColumnLimit: 120
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: LogicalBlock
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
IndentAccessModifiers: false
IndentCaseBlocks: false
IndentCaseLabels: true
IndentExternBlock: Indent
IndentGotoLabels: false
IndentRequiresClause: true
IndentWidth: 4
IndentWrappedFunctionNames: true
InsertBraces: false
InsertNewlineAtEOF: true
KeepEmptyLinesAtTheStartOfBlocks: false
LambdaBodyIndentation: Signature
NamespaceIndentation: All
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 4
ObjCBreakBeforeNestedBlockParam: false
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: true
PPIndentWidth: -1
PackConstructorInitializers: NextLine
QualifierAlignment: Leave
ReferenceAlignment: Right
RemoveSemicolon: false
RequiresClausePosition: WithPreceding
RequiresExpressionIndentation: OuterScope
SeparateDefinitionBlocks: Leave
ShortNamespaceLines: 1
SortIncludes: false
#SortUsingDeclarations: LexicographicNumeric
SortUsingDeclarations: true
SpaceAfterCStyleCast: true
SpaceAfterLogicalNot: false
SpaceAroundPointerQualifiers: Default
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: true
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 2
SpacesInConditionalStatement: false
SpacesInLineCommentPrefix:
Minimum: 1
Maximum: -1
Standard: c++17
TabWidth: 4
UseTab: Never
07070100000001000081A400000000000000000000000168D5836B00000448000000000000000000000000000000000000004800000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/.cmake-format.json{
"format": {
"line_width": 120,
"tab_size": 2,
"enable_sort": true,
"autosort": true
},
"additional_commands": {
"find_qt": {
"flags": [],
"kwargs": {
"COMPONENTS": "+",
"COMPONENTS_WIN": "+",
"COMPONENTS_MACOS": "+",
"COMPONENTS_LINUX": "+"
}
},
"set_target_properties_obs": {
"pargs": 1,
"flags": [],
"kwargs": {
"PROPERTIES": {
"kwargs": {
"PREFIX": 1,
"OUTPUT_NAME": 1,
"FOLDER": 1,
"VERSION": 1,
"SOVERSION": 1,
"FRAMEWORK": 1,
"BUNDLE": 1,
"AUTOMOC": 1,
"AUTOUIC": 1,
"AUTORCC": 1,
"AUTOUIC_SEARCH_PATHS": 1,
"BUILD_RPATH": 1,
"INSTALL_RPATH": 1,
"XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC": 1,
"XCODE_ATTRIBUTE_CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION": 1,
"XCODE_ATTRIBUTE_GCC_WARN_SHADOW":1 ,
"LIBRARY_OUTPUT_DIRECTORY": 1
}
}
}
}
}
}
07070100000002000041ED00000000000000000000000268D5836B00000000000000000000000000000000000000000000003D00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/.github07070100000003000041ED00000000000000000000000268D5836B00000000000000000000000000000000000000000000004500000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/.github/actions07070100000004000041ED00000000000000000000000268D5836B00000000000000000000000000000000000000000000005300000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/.github/actions/check-changes07070100000005000081A400000000000000000000000168D5836B00000B88000000000000000000000000000000000000005F00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/.github/actions/check-changes/action.yamlname: Check For Changed Files
description: Checks for changed files compared to specific git reference and glob expression
inputs:
baseRef:
description: Git reference to check against
required: false
ref:
description: Git reference to check with
required: false
default: HEAD
checkGlob:
description: Glob expression to limit check to specific files
required: false
useFallback:
description: Use fallback compare against prior commit
required: false
default: 'true'
diffFilter:
description: git diff-filter string to use
required: false
default: ''
outputs:
hasChangedFiles:
value: ${{ steps.checks.outputs.hasChangedFiles }}
description: True if specified files were changed in comparison to specified git reference
changedFiles:
value: ${{ steps.checks.outputs.changedFiles }}
description: List of changed files
runs:
using: composite
steps:
- name: Check For Changed Files ✅
shell: bash
id: checks
env:
GIT_BASE_REF: ${{ inputs.baseRef }}
GIT_REF: ${{ inputs.ref }}
GITHUB_EVENT_FORCED: ${{ github.event.forced }}
GITHUB_REF_BEFORE: ${{ github.event.before }}
USE_FALLBACK: ${{ inputs.useFallback }}
DIFF_FILTER: ${{ inputs.diffFilter }}
run: |
: Check for Changed Files ✅
if [[ "${RUNNER_DEBUG}" ]]; then set -x; fi
shopt -s extglob
shopt -s dotglob
# 4b825dc642cb6eb9a060e54bf8d69288fbee4904 is a "hidden" sha1 hash of
# the "empty tree", retrived via 'git hash-object -t tree /dev/null',
# and used here as a last-resort fallback to always provide a valid
# git ref.
if [[ "${GIT_BASE_REF}" ]]; then
if ! git cat-file -e "${GIT_BASE_REF}" &> /dev/null; then
echo "::warning::Provided base reference ${GIT_BASE_REF} is invalid"
if [[ "${USE_FALLBACK}" == 'true' ]]; then
GIT_BASE_REF='HEAD~1'
fi
fi
else
if ! git cat-file -e ${GITHUB_REF_BEFORE} &> /dev/null; then
GITHUB_REF_BEFORE='4b825dc642cb6eb9a060e54bf8d69288fbee4904'
fi
GIT_BASE_REF='HEAD~1'
case "${GITHUB_EVENT_NAME}" in
pull_request) GIT_BASE_REF="origin/${GITHUB_BASE_REF}" ;;
push) if [[ "${GITHUB_EVENT_FORCED}" != 'true' ]]; then GIT_BASE_REF="${GITHUB_REF_BEFORE}"; fi ;;
*) ;;
esac
fi
changes=($(git diff --name-only --diff-filter="${DIFF_FILTER}" ${GIT_BASE_REF} ${GIT_REF} -- ${{ inputs.checkGlob }}))
if (( ${#changes[@]} )); then
file_string="${changes[*]}"
echo "hasChangedFiles=true" >> $GITHUB_OUTPUT
echo "changedFiles=[\"${file_string// /\",\"}\"]" >> $GITHUB_OUTPUT
else
echo "hasChangedFiles=false" >> $GITHUB_OUTPUT
echo "changedFiles=[]" >> GITHUB_OUTPUT
fi
07070100000006000041ED00000000000000000000000268D5836B00000000000000000000000000000000000000000000005600000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/.github/actions/run-clang-format07070100000007000081A400000000000000000000000168D5836B00000878000000000000000000000000000000000000006200000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/.github/actions/run-clang-format/action.yamlname: Run clang-format
description: Runs clang-format and checks for any changes introduced by it
inputs:
failCondition:
description: Controls whether failed checks also fail the workflow run
required: false
default: never
workingDirectory:
description: Working directory for checks
required: false
default: ${{ github.workspace }}
runs:
using: composite
steps:
- name: Check Runner Operating System 🏃♂️
if: runner.os == 'Windows'
shell: bash
run: |
: Check Runner Operating System 🏃♂️
echo "::notice::run-clang-format action requires a macOS-based or Linux-based runner."
exit 2
- name: Check for Changed Files ✅
uses: ./.github/actions/check-changes
id: checks
with:
checkGlob: "'*.c' '*.h' '*.cpp' '*.hpp' '*.m' '*.mm'"
diffFilter: 'ACM'
- name: Install Dependencies 🛍️
if: runner.os == 'Linux' && fromJSON(steps.checks.outputs.hasChangedFiles)
shell: bash
run: |
: Install Dependencies 🛍️
echo ::group::Install Dependencies
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
echo "/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin" >> $GITHUB_PATH
echo "/home/linuxbrew/.linuxbrew/opt/clang-format@19/bin" >> $GITHUB_PATH
brew install --quiet zsh
echo ::endgroup::
- name: Run clang-format 🐉
if: fromJSON(steps.checks.outputs.hasChangedFiles)
id: result
shell: zsh --no-rcs --errexit --pipefail {0}
working-directory: ${{ inputs.workingDirectory }}
env:
CHANGED_FILES: ${{ steps.checks.outputs.changedFiles }}
run: |
: Run clang-format 🐉
if (( ${+RUNNER_DEBUG} )) setopt XTRACE
print ::group::Install clang-format-19
brew install --quiet obsproject/tools/clang-format@19
print ::endgroup::
print ::group::Run clang-format-19
local -a changes=(${(s:,:)CHANGED_FILES//[\[\]\'\"]/})
./build-aux/run-clang-format --fail-${{ inputs.failCondition }} --check ${changes}
print ::endgroup::
07070100000008000041ED00000000000000000000000268D5836B00000000000000000000000000000000000000000000004700000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/.github/workflows07070100000009000081A400000000000000000000000168D5836B0000015A000000000000000000000000000000000000005900000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/.github/workflows/check-format.yamlname: Check Code Formatting 🛠️
on:
workflow_call:
jobs:
clang-format:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: clang-format Check 🐉
id: clang-format
uses: ./.github/actions/run-clang-format
with:
failCondition: error
0707010000000A000081A400000000000000000000000168D5836B0000021A000000000000000000000000000000000000005400000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/.github/workflows/pr-pull.yamlname: Pull
run-name: ${{ github.event.pull_request.title }} pull request run 🚀
on:
workflow_dispatch:
pull_request:
paths-ignore:
- '**.md'
branches: [master]
types: [ opened, synchronize, reopened ]
permissions:
contents: read
concurrency:
group: '${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}'
cancel-in-progress: true
jobs:
check-format:
name: Format 🔍
uses: ./.github/workflows/check-format.yaml
permissions:
contents: read
0707010000000B000081A400000000000000000000000168D5836B00000175000000000000000000000000000000000000005100000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/.github/workflows/push.yamlname: Push
run-name: ${{ github.ref_name }} push run 🚀
on:
push:
paths-ignore:
- '**.md'
branches:
- master
- 'release/**'
tags:
- '*'
permissions:
contents: write
jobs:
check-format:
name: Format 🔍
if: github.ref_name == 'master'
uses: ./.github/workflows/check-format.yaml
permissions:
contents: read
0707010000000C000081A400000000000000000000000168D5836B000002B1000000000000000000000000000000000000004000000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/.gitignore#binaries
*.exe
*.dll
*.dylib
*.so
#cmake
/cmbuild/
/build/
/build32/
/build64/
/release/
/release32/
/release64/
/debug/
/debug32/
/debug64/
/builds/
*.o.d
*.ninja
.ninja*
.dirstamp
#xcode
*.xcodeproj/
#other stuff (windows stuff, qt moc stuff, etc)
Release_MD/
Release/
Debug/
x64/
ipch/
GeneratedFiles/
.moc/
/other/
#make stuff
configure
depcomp
install-sh
Makefile.in
Makefile
#random useless file stuff
*.dmg
*.app
.DS_Store
.directory
.hg
.depend
tags
*.trace
*.vsp
*.psess
*.swp
*.dat
*.clbin
*.log
*.tlog
*.sdf
*.opensdf
*.xml
*.ipch
*.css
*.xslt
*.aps
*.suo
*.ncb
*.user
*.lo
*.ilk
*.la
*.o
*.obj
*.pdb
*.res
*.dep
*.zip
*.lnk
*.chm
*~
.DS_Store
*/.DS_Store
*/**/.DS_Store
0707010000000D000081A400000000000000000000000168D5836B000006E2000000000000000000000000000000000000004400000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/CMakeLists.txtcmake_minimum_required(VERSION 3.16...3.25)
option(ENABLE_BROWSER "Enable browser source plugin (required Chromium Embedded Framework)" OFF)
if(NOT ENABLE_BROWSER)
target_disable(obs-browser)
target_disable_feature(obs-browser "Browser sources are not enabled by default (set CEF_ROOT_DIR and ENABLE_BROWSER)")
return()
endif()
find_package(CEF 95 REQUIRED)
find_package(nlohmann_json 3.11 REQUIRED)
add_library(obs-browser MODULE)
add_library(OBS::browser ALIAS obs-browser)
option(ENABLE_BROWSER_PANELS "Enable Qt web browser panel support" ON)
mark_as_advanced(ENABLE_BROWSER_PANELS)
target_sources(
obs-browser
PRIVATE # cmake-format: sortable
browser-app.cpp
browser-app.hpp
browser-client.cpp
browser-client.hpp
browser-scheme.cpp
browser-scheme.hpp
browser-version.h
cef-headers.hpp
deps/base64/base64.cpp
deps/base64/base64.hpp
deps/signal-restore.cpp
deps/signal-restore.hpp
deps/wide-string.cpp
deps/wide-string.hpp
obs-browser-plugin.cpp
obs-browser-source.cpp
obs-browser-source.hpp)
target_include_directories(obs-browser PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/deps")
target_compile_features(obs-browser PRIVATE cxx_std_17)
target_link_libraries(obs-browser PRIVATE OBS::libobs OBS::frontend-api OBS::websocket-api nlohmann_json::nlohmann_json)
if(OS_WINDOWS)
include(cmake/os-windows.cmake)
elseif(OS_MACOS)
include(cmake/os-macos.cmake)
elseif(OS_LINUX)
include(cmake/os-linux.cmake)
endif()
if(ENABLE_BROWSER_PANELS)
include(cmake/feature-panels.cmake)
endif()
set_target_properties_obs(obs-browser PROPERTIES FOLDER plugins/obs-browser PREFIX "")
0707010000000E000081A400000000000000000000000168D5836B00004648000000000000000000000000000000000000003D00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/COPYING GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The 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 Library General Public License instead.) You can apply it to
your programs, too.
When 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.
To 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.
For 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.
We 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.
Also, 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.
Finally, 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.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. 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".
Activities 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.
1. 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.
You 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.
2. 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:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) 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.
c) 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.)
These 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.
Thus, 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.
In 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.
3. 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:
a) 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,
b) 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,
c) 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.)
The 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.
If 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.
4. 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.
5. 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.
6. 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.
7. 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.
If 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.
It 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.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. 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.
9. 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.
Each 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.
10. 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.
NO WARRANTY
11. 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.
12. 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.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.
0707010000000F000081A400000000000000000000000168D5836B00002433000000000000000000000000000000000000003F00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/README.md# obs-browser
obs-browser introduces a cross-platform Browser Source, powered by CEF ([Chromium Embedded Framework](https://bitbucket.org/chromiumembedded/cef/src/master/README.md)), to OBS Studio. A Browser Source allows the user to integrate web-based overlays into their scenes, with complete access to modern web APIs.
Additionally, obs-browser enables Service Integration (linking third party services) and Browser Docks (webpages loaded into the interface itself) on all supported platforms, except for Wayland (Linux).
**This plugin is included by default** on official packages on Windows, macOS, the Ubuntu PPA and the official [Flatpak](https://flathub.org/apps/details/com.obsproject.Studio) (most Linux distributions).
## JS Bindings
obs-browser provides a global object that allows access to some OBS-specific functionality from JavaScript. This can be used to create an overlay that adapts dynamically to changes in OBS.
### TypeScript Type Definitions
If you're using TypeScript, type definitions for the obs-browser bindings are available through npm and yarn.
```sh
# npm
npm install --save-dev @types/obs-studio
# yarn
yarn add --dev @types/obs-studio
```
### Get Browser Plugin Version
```js
/**
* @returns {string} OBS Browser plugin version
*/
window.obsstudio.pluginVersion
// => 2.17.0
```
### Register for event callbacks
```js
/**
* @callback EventListener
* @param {CustomEvent} event
*/
/**
* @param {string} type
* @param {EventListener} listener
*/
window.addEventListener('obsSceneChanged', function(event) {
var t = document.createTextNode(event.detail.name)
document.body.appendChild(t)
})
```
#### Available events
Descriptions for these events can be [found here](https://obsproject.com/docs/reference-frontend-api.html?highlight=paused#c.obs_frontend_event).
* obsSceneChanged
* obsSceneListChanged
* obsTransitionChanged
* obsTransitionListChanged
* obsSourceVisibleChanged
* obsSourceActiveChanged
* obsStreamingStarting
* obsStreamingStarted
* obsStreamingStopping
* obsStreamingStopped
* obsRecordingStarting
* obsRecordingStarted
* obsRecordingPaused
* obsRecordingUnpaused
* obsRecordingStopping
* obsRecordingStopped
* obsReplaybufferStarting
* obsReplaybufferStarted
* obsReplaybufferSaved
* obsReplaybufferStopping
* obsReplaybufferStopped
* obsVirtualcamStarted
* obsVirtualcamStopped
* obsExit
* [Any custom event emitted via obs-websocket vendor requests]
### Control OBS
#### Get webpage control permissions
Permissions required: NONE
```js
/**
* @typedef {number} Level - The level of permissions. 0 for NONE, 1 for READ_OBS (OBS data), 2 for READ_USER (User data), 3 for BASIC, 4 for ADVANCED and 5 for ALL
*/
/**
* @callback LevelCallback
* @param {Level} level
*/
/**
* @param {LevelCallback} cb - The callback that receives the current control level.
*/
window.obsstudio.getControlLevel(function (level) {
console.log(level)
})
```
#### Get OBS output status
Permissions required: READ_OBS
```js
/**
* @typedef {Object} Status
* @property {boolean} recording - not affected by pause state
* @property {boolean} recordingPaused
* @property {boolean} streaming
* @property {boolean} replaybuffer
* @property {boolean} virtualcam
*/
/**
* @callback StatusCallback
* @param {Status} status
*/
/**
* @param {StatusCallback} cb - The callback that receives the current output status of OBS.
*/
window.obsstudio.getStatus(function (status) {
console.log(status)
})
```
#### Get the current scene
Permissions required: READ_USER
```js
/**
* @typedef {Object} Scene
* @property {string} name - name of the scene
* @property {number} width - width of the scene
* @property {number} height - height of the scene
*/
/**
* @callback SceneCallback
* @param {Scene} scene
*/
/**
* @param {SceneCallback} cb - The callback that receives the current scene in OBS.
*/
window.obsstudio.getCurrentScene(function(scene) {
console.log(scene)
})
```
#### Get scenes
Permissions required: READ_USER
```js
/**
* @callback ScenesCallback
* @param {string[]} scenes
*/
/**
* @param {ScenesCallback} cb - The callback that receives the scenes.
*/
window.obsstudio.getScenes(function (scenes) {
console.log(scenes)
})
```
#### Get transitions
Permissions required: READ_USER
```js
/**
* @callback TransitionsCallback
* @param {string[]} transitions
*/
/**
* @param {TransitionsCallback} cb - The callback that receives the transitions.
*/
window.obsstudio.getTransitions(function (transitions) {
console.log(transitions)
})
```
#### Get current transition
Permissions required: READ_USER
```js
/**
* @callback TransitionCallback
* @param {string} transition
*/
/**
* @param {TransitionCallback} cb - The callback that receives the transition currently set.
*/
window.obsstudio.getCurrentTransition(function (transition) {
console.log(transition)
})
```
#### Save the Replay Buffer
Permissions required: BASIC
```js
/**
* Does not accept any parameters and does not return anything
*/
window.obsstudio.saveReplayBuffer()
```
#### Start the Replay Buffer
Permissions required: ADVANCED
```js
/**
* Does not accept any parameters and does not return anything
*/
window.obsstudio.startReplayBuffer()
```
#### Stop the Replay Buffer
Permissions required: ADVANCED
```js
/**
* Does not accept any parameters and does not return anything
*/
window.obsstudio.stopReplayBuffer()
```
#### Change scene
Permissions required: ADVANCED
```js
/**
* @param {string} name - Name of the scene
*/
window.obsstudio.setCurrentScene(name)
```
#### Set the current transition
Permissions required: ADVANCED
```js
/**
* @param {string} name - Name of the transition
*/
window.obsstudio.setCurrentTransition(name)
```
#### Start streaming
Permissions required: ALL
```js
/**
* Does not accept any parameters and does not return anything
*/
window.obsstudio.startStreaming()
```
#### Stop streaming
Permissions required: ALL
```js
/**
* Does not accept any parameters and does not return anything
*/
window.obsstudio.stopStreaming()
```
#### Start recording
Permissions required: ALL
```js
/**
* Does not accept any parameters and does not return anything
*/
window.obsstudio.startRecording()
```
#### Stop recording
Permissions required: ALL
```js
/**
* Does not accept any parameters and does not return anything
*/
window.obsstudio.stopRecording()
```
#### Pause recording
Permissions required: ALL
```js
/**
* Does not accept any parameters and does not return anything
*/
window.obsstudio.pauseRecording()
```
#### Unpause recording
Permissions required: ALL
```js
/**
* Does not accept any parameters and does not return anything
*/
window.obsstudio.unpauseRecording()
```
#### Start the Virtual Camera
Permissions required: ALL
```js
/**
* Does not accept any parameters and does not return anything
*/
window.obsstudio.startVirtualcam()
```
#### Stop the Virtual Camera
Permissions required: ALL
```js
/**
* Does not accept any parameters and does not return anything
*/
window.obsstudio.stopVirtualcam()
```
### Register for visibility callbacks
**This method is legacy. Register an event listener instead.**
```js
/**
* onVisibilityChange gets callbacks when the visibility of the browser source changes in OBS
*
* @deprecated
* @see obsSourceVisibleChanged
* @param {boolean} visibility - True -> visible, False -> hidden
*/
window.obsstudio.onVisibilityChange = function(visibility) {
};
```
### Register for active/inactive callbacks
**This method is legacy. Register an event listener instead.**
```js
/**
* onActiveChange gets callbacks when the active/inactive state of the browser source changes in OBS
*
* @deprecated
* @see obsSourceActiveChanged
* @param {bool} True -> active, False -> inactive
*/
window.obsstudio.onActiveChange = function(active) {
};
```
### obs-websocket Vendor
obs-browser includes integration with obs-websocket's Vendor requests. The vendor name to use is `obs-browser`, and available requests are:
- `emit_event` - Takes `event_name` and ?`event_data` parameters. Emits a custom event to all browser sources. To subscribe to events, see [here](#register-for-event-callbacks)
- See [#340](https://github.com/obsproject/obs-browser/pull/340) for example usage.
There are no available vendor events at this time.
## Building
OBS Browser cannot be built standalone. It is built as part of OBS Studio.
By following the instructions, this will enable Browser Source & Custom Browser Docks on all three platforms. Both `BUILD_BROWSER` and `CEF_ROOT_DIR` are required.
### On Windows
Follow the [build instructions](https://obsproject.com/wiki/Install-Instructions#windows-build-directions) and be sure to download the **CEF Wrapper** and set `CEF_ROOT_DIR` in CMake to point to the extracted wrapper.
### On macOS
Use the [macOS Full Build Script](https://obsproject.com/wiki/Install-Instructions#macos-build-directions). This will automatically download & enable OBS Browser.
### On Linux
Follow the [build instructions](https://obsproject.com/wiki/Install-Instructions#linux-build-directions) and choose the "If building with browser source" option. This includes steps to download/extract the CEF Wrapper, and set the required CMake variables.
07070100000010000081A400000000000000000000000168D5836B000030E9000000000000000000000000000000000000004500000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/browser-app.cpp/******************************************************************************
Copyright (C) 2014 by John R. Bradley <jrb@turrettech.com>
Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
#include "browser-app.hpp"
#include "browser-version.h"
#include <nlohmann/json.hpp>
#ifdef _WIN32
#include <windows.h>
#endif
#ifdef ENABLE_BROWSER_QT_LOOP
#include <util/base.h>
#include <util/platform.h>
#include <util/threading.h>
#include <QTimer>
#endif
#ifndef UNUSED_PARAMETER
#define UNUSED_PARAMETER(x) \
{ \
(void)x; \
}
#endif
CefRefPtr<CefRenderProcessHandler> BrowserApp::GetRenderProcessHandler()
{
return this;
}
CefRefPtr<CefBrowserProcessHandler> BrowserApp::GetBrowserProcessHandler()
{
return this;
}
void BrowserApp::OnRegisterCustomSchemes(CefRawPtr<CefSchemeRegistrar> registrar)
{
registrar->AddCustomScheme("http", CEF_SCHEME_OPTION_STANDARD | CEF_SCHEME_OPTION_CORS_ENABLED);
}
void BrowserApp::OnBeforeChildProcessLaunch(CefRefPtr<CefCommandLine> command_line)
{
#ifdef _WIN32
std::string pid = std::to_string(GetCurrentProcessId());
command_line->AppendSwitchWithValue("parent_pid", pid);
#else
(void)command_line;
#endif
}
void BrowserApp::OnBeforeCommandLineProcessing(const CefString &, CefRefPtr<CefCommandLine> command_line)
{
if (!shared_texture_available) {
bool enableGPU = command_line->HasSwitch("enable-gpu");
CefString type = command_line->GetSwitchValue("type");
if (!enableGPU && type.empty()) {
command_line->AppendSwitch("disable-gpu-compositing");
}
}
if (command_line->HasSwitch("disable-features")) {
// Don't override existing, as this can break OSR
std::string disableFeatures = command_line->GetSwitchValue("disable-features");
disableFeatures += ",HardwareMediaKeyHandling";
#ifdef _WIN32
disableFeatures += ",EnableWindowsGamingInputDataFetcher";
#endif
disableFeatures += ",WebBluetooth";
command_line->AppendSwitchWithValue("disable-features", disableFeatures);
} else {
command_line->AppendSwitchWithValue("disable-features", "WebBluetooth,"
#ifdef _WIN32
"EnableWindowsGamingInputDataFetcher,"
#endif
"HardwareMediaKeyHandling");
}
command_line->AppendSwitchWithValue("autoplay-policy", "no-user-gesture-required");
#ifdef __APPLE__
command_line->AppendSwitch("use-mock-keychain");
#elif !defined(_WIN32)
command_line->AppendSwitchWithValue("ozone-platform", wayland ? "wayland" : "x11");
#endif
}
std::vector<std::string> exposedFunctions = {"getControlLevel", "getCurrentScene", "getStatus",
"startRecording", "stopRecording", "startStreaming",
"stopStreaming", "pauseRecording", "unpauseRecording",
"startReplayBuffer", "stopReplayBuffer", "saveReplayBuffer",
"startVirtualcam", "stopVirtualcam", "getScenes",
"setCurrentScene", "getTransitions", "getCurrentTransition",
"setCurrentTransition"};
void BrowserApp::OnContextCreated(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame>, CefRefPtr<CefV8Context> context)
{
CefRefPtr<CefV8Value> globalObj = context->GetGlobal();
CefRefPtr<CefV8Value> obsStudioObj = CefV8Value::CreateObject(nullptr, nullptr);
globalObj->SetValue("obsstudio", obsStudioObj, V8_PROPERTY_ATTRIBUTE_NONE);
CefRefPtr<CefV8Value> pluginVersion = CefV8Value::CreateString(OBS_BROWSER_VERSION_STRING);
obsStudioObj->SetValue("pluginVersion", pluginVersion, V8_PROPERTY_ATTRIBUTE_NONE);
for (std::string name : exposedFunctions) {
CefRefPtr<CefV8Value> func = CefV8Value::CreateFunction(name, this);
obsStudioObj->SetValue(name, func, V8_PROPERTY_ATTRIBUTE_NONE);
}
UNUSED_PARAMETER(browser);
}
void BrowserApp::ExecuteJSFunction(CefRefPtr<CefBrowser> browser, const char *functionName, CefV8ValueList arguments)
{
std::vector<CefString> names;
browser->GetFrameNames(names);
for (auto &name : names) {
CefRefPtr<CefFrame> frame =
#if CHROME_VERSION_BUILD >= 6261
browser->GetFrameByName(name);
#else
browser->GetFrame(name);
#endif
CefRefPtr<CefV8Context> context = frame->GetV8Context();
context->Enter();
CefRefPtr<CefV8Value> globalObj = context->GetGlobal();
CefRefPtr<CefV8Value> obsStudioObj = globalObj->GetValue("obsstudio");
CefRefPtr<CefV8Value> jsFunction = obsStudioObj->GetValue(functionName);
if (jsFunction && jsFunction->IsFunction())
jsFunction->ExecuteFunction(nullptr, arguments);
context->Exit();
}
}
CefRefPtr<CefV8Value> CefValueToCefV8Value(CefRefPtr<CefValue> value)
{
CefRefPtr<CefV8Value> result;
switch (value->GetType()) {
case VTYPE_INVALID:
result = CefV8Value::CreateNull();
break;
case VTYPE_NULL:
result = CefV8Value::CreateNull();
break;
case VTYPE_BOOL:
result = CefV8Value::CreateBool(value->GetBool());
break;
case VTYPE_INT:
result = CefV8Value::CreateInt(value->GetInt());
break;
case VTYPE_DOUBLE:
result = CefV8Value::CreateDouble(value->GetDouble());
break;
case VTYPE_STRING:
result = CefV8Value::CreateString(value->GetString());
break;
case VTYPE_BINARY:
result = CefV8Value::CreateNull();
break;
case VTYPE_DICTIONARY: {
result = CefV8Value::CreateObject(nullptr, nullptr);
CefRefPtr<CefDictionaryValue> dict = value->GetDictionary();
CefDictionaryValue::KeyList keys;
dict->GetKeys(keys);
for (unsigned int i = 0; i < keys.size(); i++) {
CefString key = keys[i];
result->SetValue(key, CefValueToCefV8Value(dict->GetValue(key)), V8_PROPERTY_ATTRIBUTE_NONE);
}
} break;
case VTYPE_LIST: {
CefRefPtr<CefListValue> list = value->GetList();
size_t size = list->GetSize();
result = CefV8Value::CreateArray((int)size);
for (size_t i = 0; i < size; i++) {
result->SetValue((int)i, CefValueToCefV8Value(list->GetValue(i)));
}
} break;
#if !defined(_WIN32) && CHROME_VERSION_BUILD >= 6943
case VTYPE_NUM_VALUES:
result = CefV8Value::CreateNull();
break;
#endif
}
return result;
}
bool BrowserApp::OnProcessMessageReceived(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame,
CefProcessId source_process, CefRefPtr<CefProcessMessage> message)
{
UNUSED_PARAMETER(frame);
DCHECK(source_process == PID_BROWSER);
CefRefPtr<CefListValue> args = message->GetArgumentList();
if (message->GetName() == "Visibility") {
CefV8ValueList arguments;
arguments.push_back(CefV8Value::CreateBool(args->GetBool(0)));
ExecuteJSFunction(browser, "onVisibilityChange", arguments);
} else if (message->GetName() == "Active") {
CefV8ValueList arguments;
arguments.push_back(CefV8Value::CreateBool(args->GetBool(0)));
ExecuteJSFunction(browser, "onActiveChange", arguments);
} else if (message->GetName() == "DispatchJSEvent") {
nlohmann::json payloadJson = nlohmann::json::parse(args->GetString(1).ToString(), nullptr, false);
nlohmann::json wrapperJson;
if (args->GetSize() > 1)
wrapperJson["detail"] = payloadJson;
std::string wrapperJsonString = wrapperJson.dump();
std::string script;
script += "new CustomEvent('";
script += args->GetString(0).ToString();
script += "', ";
script += wrapperJsonString;
script += ");";
std::vector<CefString> names;
browser->GetFrameNames(names);
for (auto &name : names) {
CefRefPtr<CefFrame> frame =
#if CHROME_VERSION_BUILD >= 6261
browser->GetFrameByName(name);
#else
browser->GetFrame(name);
#endif
CefRefPtr<CefV8Context> context = frame->GetV8Context();
context->Enter();
CefRefPtr<CefV8Value> globalObj = context->GetGlobal();
CefRefPtr<CefV8Value> returnValue;
CefRefPtr<CefV8Exception> exception;
/* Create the CustomEvent object
* We have to use eval to invoke the new operator */
context->Eval(script, browser->GetMainFrame()->GetURL(), 0, returnValue, exception);
CefV8ValueList arguments;
arguments.push_back(returnValue);
CefRefPtr<CefV8Value> dispatchEvent = globalObj->GetValue("dispatchEvent");
dispatchEvent->ExecuteFunction(nullptr, arguments);
context->Exit();
}
} else if (message->GetName() == "executeCallback") {
CefRefPtr<CefV8Context> context = browser->GetMainFrame()->GetV8Context();
context->Enter();
CefRefPtr<CefListValue> arguments = message->GetArgumentList();
int callbackID = arguments->GetInt(0);
CefString jsonString = arguments->GetString(1);
CefRefPtr<CefValue> json = CefParseJSON(arguments->GetString(1).ToString(), {});
CefRefPtr<CefV8Value> callback = callbackMap[callbackID];
CefV8ValueList args;
CefRefPtr<CefV8Value> retval = CefValueToCefV8Value(json);
args.push_back(retval);
if (callback)
callback->ExecuteFunction(nullptr, args);
context->Exit();
callbackMap.erase(callbackID);
} else {
return false;
}
return true;
}
bool IsValidFunction(std::string function)
{
std::vector<std::string>::iterator iterator;
iterator = std::find(exposedFunctions.begin(), exposedFunctions.end(), function);
return iterator != exposedFunctions.end();
}
bool BrowserApp::Execute(const CefString &name, CefRefPtr<CefV8Value>, const CefV8ValueList &arguments,
CefRefPtr<CefV8Value> &, CefString &)
{
if (IsValidFunction(name.ToString())) {
if (arguments.size() >= 1 && arguments[0]->IsFunction()) {
callbackId++;
callbackMap[callbackId] = arguments[0];
}
CefRefPtr<CefProcessMessage> msg = CefProcessMessage::Create(name);
CefRefPtr<CefListValue> args = msg->GetArgumentList();
args->SetInt(0, callbackId);
/* Pass on arguments */
for (u_long l = 0; l < arguments.size(); l++) {
u_long pos;
if (arguments[0]->IsFunction())
pos = l;
else
pos = l + 1;
if (arguments[l]->IsString())
args->SetString(pos, arguments[l]->GetStringValue());
else if (arguments[l]->IsInt())
args->SetInt(pos, arguments[l]->GetIntValue());
else if (arguments[l]->IsBool())
args->SetBool(pos, arguments[l]->GetBoolValue());
else if (arguments[l]->IsDouble())
args->SetDouble(pos, arguments[l]->GetDoubleValue());
}
CefRefPtr<CefBrowser> browser = CefV8Context::GetCurrentContext()->GetBrowser();
SendBrowserProcessMessage(browser, PID_BROWSER, msg);
} else {
/* Function does not exist. */
return false;
}
return true;
}
#ifdef ENABLE_BROWSER_QT_LOOP
Q_DECLARE_METATYPE(MessageTask);
MessageObject messageObject;
void QueueBrowserTask(CefRefPtr<CefBrowser> browser, BrowserFunc func)
{
std::lock_guard<std::mutex> lock(messageObject.browserTaskMutex);
messageObject.browserTasks.emplace_back(browser, func);
QMetaObject::invokeMethod(&messageObject, "ExecuteNextBrowserTask", Qt::QueuedConnection);
}
bool MessageObject::ExecuteNextBrowserTask()
{
Task nextTask;
{
std::lock_guard<std::mutex> lock(browserTaskMutex);
if (!browserTasks.size())
return false;
nextTask = browserTasks[0];
browserTasks.pop_front();
}
nextTask.func(nextTask.browser);
return true;
}
void MessageObject::ExecuteTask(MessageTask task)
{
task();
}
void MessageObject::DoCefMessageLoop(int ms)
{
if (ms)
QTimer::singleShot((int)ms + 2, []() { CefDoMessageLoopWork(); });
else
CefDoMessageLoopWork();
}
void MessageObject::Process()
{
CefDoMessageLoopWork();
}
void ProcessCef()
{
QMetaObject::invokeMethod(&messageObject, "DoCefMessageLoop", Qt::QueuedConnection, Q_ARG(int, (int)0));
}
#define MAX_DELAY (1000 / 30)
#if CHROME_VERSION_BUILD < 5938
void BrowserApp::OnScheduleMessagePumpWork(int64 delay_ms)
#else
void BrowserApp::OnScheduleMessagePumpWork(int64_t delay_ms)
#endif
{
if (delay_ms < 0)
delay_ms = 0;
else if (delay_ms > MAX_DELAY)
delay_ms = MAX_DELAY;
if (!frameTimer.isActive()) {
QObject::connect(&frameTimer, &QTimer::timeout, &messageObject, &MessageObject::Process);
frameTimer.setSingleShot(false);
frameTimer.start(33);
}
QMetaObject::invokeMethod(&messageObject, "DoCefMessageLoop", Qt::QueuedConnection, Q_ARG(int, (int)delay_ms));
}
#endif
07070100000011000081A400000000000000000000000168D5836B00000F23000000000000000000000000000000000000004500000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/browser-app.hpp/******************************************************************************
Copyright (C) 2014 by John R. Bradley <jrb@turrettech.com>
Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
#pragma once
#include <map>
#include <unordered_map>
#include <functional>
#include "cef-headers.hpp"
typedef std::function<void(CefRefPtr<CefBrowser>)> BrowserFunc;
#ifdef ENABLE_BROWSER_QT_LOOP
#include <QObject>
#include <QTimer>
#include <mutex>
#include <deque>
typedef std::function<void()> MessageTask;
class MessageObject : public QObject {
Q_OBJECT
friend void QueueBrowserTask(CefRefPtr<CefBrowser> browser, BrowserFunc func);
struct Task {
CefRefPtr<CefBrowser> browser;
BrowserFunc func;
inline Task() {}
inline Task(CefRefPtr<CefBrowser> browser_, BrowserFunc func_) : browser(browser_), func(func_) {}
};
std::mutex browserTaskMutex;
std::deque<Task> browserTasks;
public slots:
bool ExecuteNextBrowserTask();
void ExecuteTask(MessageTask task);
void DoCefMessageLoop(int ms);
void Process();
};
extern void QueueBrowserTask(CefRefPtr<CefBrowser> browser, BrowserFunc func);
#endif
class BrowserApp : public CefApp, public CefRenderProcessHandler, public CefBrowserProcessHandler, public CefV8Handler {
void ExecuteJSFunction(CefRefPtr<CefBrowser> browser, const char *functionName, CefV8ValueList arguments);
typedef std::map<int, CefRefPtr<CefV8Value>> CallbackMap;
bool shared_texture_available;
CallbackMap callbackMap;
int callbackId;
#if !defined(__APPLE__) && !defined(_WIN32)
bool wayland;
#endif
public:
#if defined(__APPLE__) || defined(_WIN32)
inline BrowserApp(bool shared_texture_available_ = false) : shared_texture_available(shared_texture_available_)
#else
inline BrowserApp(bool shared_texture_available_ = false, bool wayland_ = false)
: shared_texture_available(shared_texture_available_),
wayland(wayland_)
#endif
{
}
virtual CefRefPtr<CefRenderProcessHandler> GetRenderProcessHandler() override;
virtual CefRefPtr<CefBrowserProcessHandler> GetBrowserProcessHandler() override;
virtual void OnBeforeChildProcessLaunch(CefRefPtr<CefCommandLine> command_line) override;
virtual void OnRegisterCustomSchemes(CefRawPtr<CefSchemeRegistrar> registrar) override;
virtual void OnBeforeCommandLineProcessing(const CefString &process_type,
CefRefPtr<CefCommandLine> command_line) override;
virtual void OnContextCreated(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) override;
virtual bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame,
CefProcessId source_process,
CefRefPtr<CefProcessMessage> message) override;
virtual bool Execute(const CefString &name, CefRefPtr<CefV8Value> object, const CefV8ValueList &arguments,
CefRefPtr<CefV8Value> &retval, CefString &exception) override;
#ifdef ENABLE_BROWSER_QT_LOOP
#if CHROME_VERSION_BUILD < 5938
virtual void OnScheduleMessagePumpWork(int64 delay_ms) override;
#else
virtual void OnScheduleMessagePumpWork(int64_t delay_ms) override;
#endif
QTimer frameTimer;
#endif
IMPLEMENT_REFCOUNTING(BrowserApp);
};
07070100000012000081A400000000000000000000000168D5836B00004C15000000000000000000000000000000000000004800000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/browser-client.cpp/******************************************************************************
Copyright (C) 2014 by John R. Bradley <jrb@turrettech.com>
Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
#include "browser-client.hpp"
#include "obs-browser-source.hpp"
#include "base64/base64.hpp"
#include <nlohmann/json.hpp>
#include <obs-frontend-api.h>
#include <obs.hpp>
#include <util/platform.h>
#include <QApplication>
#include <QThread>
#include <QToolTip>
#if defined(__APPLE__) && CHROME_VERSION_BUILD > 4430
#include <IOSurface/IOSurface.h>
#endif
#if !defined(_WIN32) && !defined(__APPLE__)
#include <obs-nix-platform.h>
#include "drm-format.hpp"
#endif
inline bool BrowserClient::valid() const
{
return !!bs && !bs->destroying;
}
CefRefPtr<CefLoadHandler> BrowserClient::GetLoadHandler()
{
return this;
}
CefRefPtr<CefRenderHandler> BrowserClient::GetRenderHandler()
{
return this;
}
CefRefPtr<CefDisplayHandler> BrowserClient::GetDisplayHandler()
{
return this;
}
CefRefPtr<CefLifeSpanHandler> BrowserClient::GetLifeSpanHandler()
{
return this;
}
CefRefPtr<CefContextMenuHandler> BrowserClient::GetContextMenuHandler()
{
return this;
}
CefRefPtr<CefAudioHandler> BrowserClient::GetAudioHandler()
{
return reroute_audio ? this : nullptr;
}
CefRefPtr<CefRequestHandler> BrowserClient::GetRequestHandler()
{
return this;
}
CefRefPtr<CefResourceRequestHandler> BrowserClient::GetResourceRequestHandler(CefRefPtr<CefBrowser>,
CefRefPtr<CefFrame>,
CefRefPtr<CefRequest> request, bool, bool,
const CefString &, bool &)
{
if (request->GetHeaderByName("origin") == "null") {
return this;
}
return nullptr;
}
void BrowserClient::OnRenderProcessTerminated(CefRefPtr<CefBrowser>, TerminationStatus
#if CHROME_VERSION_BUILD >= 6367
,
int, const CefString &error_string
#endif
)
{
if (!valid())
return;
#if CHROME_VERSION_BUILD >= 6367
std::string str_text = error_string;
#else
std::string str_text = "<unknown>";
#endif
const char *sourceName = "<unknown>";
if (bs && bs->source)
sourceName = obs_source_get_name(bs->source);
blog(LOG_ERROR, "[obs-browser: '%s'] Webpage has crashed unexpectedly! Reason: '%s'", sourceName,
str_text.c_str());
}
CefResourceRequestHandler::ReturnValue BrowserClient::OnBeforeResourceLoad(CefRefPtr<CefBrowser>, CefRefPtr<CefFrame>,
CefRefPtr<CefRequest>,
CefRefPtr<CefCallback>)
{
return RV_CONTINUE;
}
bool BrowserClient::OnBeforePopup(CefRefPtr<CefBrowser>, CefRefPtr<CefFrame>,
#if CHROME_VERSION_BUILD >= 6834
int,
#endif
const CefString &, const CefString &, cef_window_open_disposition_t, bool,
const CefPopupFeatures &, CefWindowInfo &, CefRefPtr<CefClient> &,
CefBrowserSettings &, CefRefPtr<CefDictionaryValue> &, bool *)
{
/* block popups */
return true;
}
void BrowserClient::OnBeforeContextMenu(CefRefPtr<CefBrowser>, CefRefPtr<CefFrame>, CefRefPtr<CefContextMenuParams>,
CefRefPtr<CefMenuModel> model)
{
/* remove all context menu contributions */
model->Clear();
}
bool BrowserClient::OnProcessMessageReceived(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame>, CefProcessId,
CefRefPtr<CefProcessMessage> message)
{
const std::string &name = message->GetName();
CefRefPtr<CefListValue> input_args = message->GetArgumentList();
nlohmann::json json;
if (!valid()) {
return false;
}
// Fall-through switch, so that higher levels also have lower-level rights
switch (webpage_control_level) {
case ControlLevel::All:
if (name == "startRecording") {
obs_frontend_recording_start();
} else if (name == "stopRecording") {
obs_frontend_recording_stop();
} else if (name == "startStreaming") {
obs_frontend_streaming_start();
} else if (name == "stopStreaming") {
obs_frontend_streaming_stop();
} else if (name == "pauseRecording") {
obs_frontend_recording_pause(true);
} else if (name == "unpauseRecording") {
obs_frontend_recording_pause(false);
} else if (name == "startVirtualcam") {
obs_frontend_start_virtualcam();
} else if (name == "stopVirtualcam") {
obs_frontend_stop_virtualcam();
}
[[fallthrough]];
case ControlLevel::Advanced:
if (name == "startReplayBuffer") {
obs_frontend_replay_buffer_start();
} else if (name == "stopReplayBuffer") {
obs_frontend_replay_buffer_stop();
} else if (name == "setCurrentScene") {
const std::string scene_name = input_args->GetString(1).ToString();
OBSSourceAutoRelease source = obs_get_source_by_name(scene_name.c_str());
if (!source) {
blog(LOG_WARNING,
"Browser source '%s' tried to switch to scene '%s' which doesn't exist",
obs_source_get_name(bs->source), scene_name.c_str());
} else if (!obs_source_is_scene(source)) {
blog(LOG_WARNING, "Browser source '%s' tried to switch to '%s' which isn't a scene",
obs_source_get_name(bs->source), scene_name.c_str());
} else {
obs_frontend_set_current_scene(source);
}
} else if (name == "setCurrentTransition") {
const std::string transition_name = input_args->GetString(1).ToString();
obs_frontend_source_list transitions = {};
obs_frontend_get_transitions(&transitions);
OBSSourceAutoRelease transition;
for (size_t i = 0; i < transitions.sources.num; i++) {
obs_source_t *source = transitions.sources.array[i];
if (obs_source_get_name(source) == transition_name) {
transition = obs_source_get_ref(source);
break;
}
}
obs_frontend_source_list_free(&transitions);
if (transition)
obs_frontend_set_current_transition(transition);
else
blog(LOG_WARNING,
"Browser source '%s' tried to change the current transition to '%s' which doesn't exist",
obs_source_get_name(bs->source), transition_name.c_str());
}
[[fallthrough]];
case ControlLevel::Basic:
if (name == "saveReplayBuffer") {
obs_frontend_replay_buffer_save();
}
[[fallthrough]];
case ControlLevel::ReadUser:
if (name == "getScenes") {
struct obs_frontend_source_list list = {};
obs_frontend_get_scenes(&list);
std::vector<nlohmann::json> scenes_vector;
for (size_t i = 0; i < list.sources.num; i++) {
obs_source_t *source = list.sources.array[i];
scenes_vector.push_back(obs_source_get_name(source));
}
json = scenes_vector;
obs_frontend_source_list_free(&list);
} else if (name == "getCurrentScene") {
OBSSourceAutoRelease current_scene = obs_frontend_get_current_scene();
if (!current_scene)
return false;
const char *name = obs_source_get_name(current_scene);
if (!name)
return false;
json = {{"name", name},
{"width", obs_source_get_width(current_scene)},
{"height", obs_source_get_height(current_scene)}};
} else if (name == "getTransitions") {
struct obs_frontend_source_list list = {};
obs_frontend_get_transitions(&list);
std::vector<nlohmann::json> transitions_vector;
for (size_t i = 0; i < list.sources.num; i++) {
obs_source_t *source = list.sources.array[i];
transitions_vector.push_back(obs_source_get_name(source));
}
json = transitions_vector;
obs_frontend_source_list_free(&list);
} else if (name == "getCurrentTransition") {
OBSSourceAutoRelease source = obs_frontend_get_current_transition();
json = obs_source_get_name(source);
}
[[fallthrough]];
case ControlLevel::ReadObs:
if (name == "getStatus") {
json = {{"recording", obs_frontend_recording_active()},
{"streaming", obs_frontend_streaming_active()},
{"recordingPaused", obs_frontend_recording_paused()},
{"replaybuffer", obs_frontend_replay_buffer_active()},
{"virtualcam", obs_frontend_virtualcam_active()}};
}
[[fallthrough]];
case ControlLevel::None:
if (name == "getControlLevel") {
json = (int)webpage_control_level;
}
}
CefRefPtr<CefProcessMessage> msg = CefProcessMessage::Create("executeCallback");
CefRefPtr<CefListValue> execute_args = msg->GetArgumentList();
execute_args->SetInt(0, input_args->GetInt(0));
execute_args->SetString(1, json.dump());
SendBrowserProcessMessage(browser, PID_RENDERER, msg);
return true;
}
void BrowserClient::GetViewRect(CefRefPtr<CefBrowser>, CefRect &rect)
{
if (!valid()) {
rect.Set(0, 0, 16, 16);
return;
}
rect.Set(0, 0, bs->width < 1 ? 1 : bs->width, bs->height < 1 ? 1 : bs->height);
}
bool BrowserClient::OnTooltip(CefRefPtr<CefBrowser>, CefString &text)
{
std::string str_text = text;
QMetaObject::invokeMethod(QCoreApplication::instance()->thread(),
[str_text]() { QToolTip::showText(QCursor::pos(), str_text.c_str()); });
return true;
}
void BrowserClient::OnPaint(CefRefPtr<CefBrowser>, PaintElementType type, const RectList &, const void *buffer,
int width, int height)
{
if (type != PET_VIEW) {
// TODO Overlay texture on top of bs->texture
return;
}
#ifdef ENABLE_BROWSER_SHARED_TEXTURE
if (sharing_available) {
return;
}
#endif
if (!valid()) {
return;
}
if (bs->width != width || bs->height != height) {
obs_enter_graphics();
bs->DestroyTextures();
obs_leave_graphics();
}
if (!bs->texture && width && height) {
obs_enter_graphics();
bs->texture = gs_texture_create(width, height, GS_BGRA, 1, (const uint8_t **)&buffer, GS_DYNAMIC);
bs->width = width;
bs->height = height;
obs_leave_graphics();
} else {
obs_enter_graphics();
gs_texture_set_image(bs->texture, (const uint8_t *)buffer, width * 4, false);
obs_leave_graphics();
}
}
#ifdef ENABLE_BROWSER_SHARED_TEXTURE
void BrowserClient::UpdateExtraTexture()
{
if (bs->texture) {
const uint32_t cx = gs_texture_get_width(bs->texture);
const uint32_t cy = gs_texture_get_height(bs->texture);
const gs_color_format format = gs_texture_get_color_format(bs->texture);
const gs_color_format linear_format = gs_generalize_format(format);
if (linear_format != format) {
if (!bs->extra_texture || bs->last_format != linear_format || bs->last_cx != cx ||
bs->last_cy != cy) {
if (bs->extra_texture) {
gs_texture_destroy(bs->extra_texture);
bs->extra_texture = nullptr;
}
bs->extra_texture = gs_texture_create(cx, cy, linear_format, 1, nullptr, 0);
bs->last_cx = cx;
bs->last_cy = cy;
bs->last_format = linear_format;
}
} else if (bs->extra_texture) {
gs_texture_destroy(bs->extra_texture);
bs->extra_texture = nullptr;
bs->last_cx = 0;
bs->last_cy = 0;
bs->last_format = GS_UNKNOWN;
}
}
}
void BrowserClient::OnAcceleratedPaint(CefRefPtr<CefBrowser>, PaintElementType type, const RectList &,
#if CHROME_VERSION_BUILD >= 6367
const CefAcceleratedPaintInfo &info)
#else
void *shared_handle)
#endif
{
if (type != PET_VIEW) {
// TODO Overlay texture on top of bs->texture
return;
}
if (!valid()) {
return;
}
#if !defined(_WIN32) && !defined(__APPLE__)
if (info.plane_count == 0)
return;
struct obs_cef_video_format format = obs_cef_format_from_cef_type(info.format);
uint64_t modifier = info.modifier;
if (format.gs_format == GS_UNKNOWN)
return;
uint32_t *strides = (uint32_t *)alloca(info.plane_count * sizeof(uint32_t));
uint32_t *offsets = (uint32_t *)alloca(info.plane_count * sizeof(uint32_t));
uint64_t *modifiers = (uint64_t *)alloca(info.plane_count * sizeof(uint64_t));
int *fds = (int *)alloca(info.plane_count * sizeof(int));
/* NOTE: This a workaround under X11 where the modifier is always invalid where it can mean "no modifier" in
* Chromium's code. */
if (obs_get_nix_platform() == OBS_NIX_PLATFORM_X11_EGL && modifier == DRM_FORMAT_MOD_INVALID)
modifier = DRM_FORMAT_MOD_LINEAR;
for (size_t i = 0; i < kAcceleratedPaintMaxPlanes; i++) {
auto *plane = &info.planes[i];
strides[i] = plane->stride;
offsets[i] = plane->offset;
fds[i] = plane->fd;
modifiers[i] = modifier;
}
#endif
#if !defined(_WIN32) && CHROME_VERSION_BUILD < 6367
if (shared_handle == bs->last_handle)
return;
#endif
obs_enter_graphics();
if (bs->texture) {
#ifdef _WIN32
//gs_texture_release_sync(bs->texture, 0);
#endif
gs_texture_destroy(bs->texture);
bs->texture = nullptr;
}
#if defined(__APPLE__) && CHROME_VERSION_BUILD > 6367
bs->texture = gs_texture_create_from_iosurface((IOSurfaceRef)(uintptr_t)info.shared_texture_io_surface);
#elif defined(__APPLE__) && CHROME_VERSION_BUILD > 4183
bs->texture = gs_texture_create_from_iosurface((IOSurfaceRef)(uintptr_t)shared_handle);
#elif defined(_WIN32) && CHROME_VERSION_BUILD > 4183
bs->texture =
#if CHROME_VERSION_BUILD >= 6367
gs_texture_open_nt_shared((uint32_t)(uintptr_t)info.shared_texture_handle);
#else
gs_texture_open_nt_shared((uint32_t)(uintptr_t)shared_handle);
#endif
//if (bs->texture)
// gs_texture_acquire_sync(bs->texture, 1, INFINITE);
#elif defined(_WIN32)
bs->texture = gs_texture_open_shared((uint32_t)(uintptr_t)shared_handle);
#else
bs->texture = gs_texture_create_from_dmabuf(info.extra.coded_size.width, info.extra.coded_size.height,
format.drm_format, format.gs_format, info.plane_count, fds, strides,
offsets, modifier != DRM_FORMAT_MOD_INVALID ? modifiers : NULL);
#endif
UpdateExtraTexture();
obs_leave_graphics();
#if defined(__APPLE__) && CHROME_VERSION_BUILD >= 6367
bs->last_handle = info.shared_texture_io_surface;
#elif defined(_WIN32) && CHROME_VERSION_BUILD >= 6367
bs->last_handle = info.shared_texture_handle;
#elif defined(__APPLE__) || defined(_WIN32)
bs->last_handle = shared_handle;
#endif
}
#ifdef CEF_ON_ACCELERATED_PAINT2
void BrowserClient::OnAcceleratedPaint2(CefRefPtr<CefBrowser>, PaintElementType type, const RectList &,
void *shared_handle, bool new_texture)
{
if (type != PET_VIEW) {
// TODO Overlay texture on top of bs->texture
return;
}
if (!valid()) {
return;
}
if (!new_texture) {
return;
}
obs_enter_graphics();
if (bs->texture) {
gs_texture_destroy(bs->texture);
bs->texture = nullptr;
}
#if defined(__APPLE__) && CHROME_VERSION_BUILD > 4183
bs->texture = gs_texture_create_from_iosurface((IOSurfaceRef)(uintptr_t)shared_handle);
#elif defined(_WIN32) && CHROME_VERSION_BUILD > 4183
bs->texture = gs_texture_open_nt_shared((uint32_t)(uintptr_t)shared_handle);
#else
bs->texture = gs_texture_open_shared((uint32_t)(uintptr_t)shared_handle);
#endif
UpdateExtraTexture();
obs_leave_graphics();
}
#endif
#endif
static speaker_layout GetSpeakerLayout(CefAudioHandler::ChannelLayout cefLayout)
{
switch (cefLayout) {
case CEF_CHANNEL_LAYOUT_MONO:
return SPEAKERS_MONO; /**< Channels: MONO */
case CEF_CHANNEL_LAYOUT_STEREO:
return SPEAKERS_STEREO; /**< Channels: FL, FR */
case CEF_CHANNEL_LAYOUT_2POINT1:
case CEF_CHANNEL_LAYOUT_2_1:
case CEF_CHANNEL_LAYOUT_SURROUND:
return SPEAKERS_2POINT1; /**< Channels: FL, FR, LFE */
case CEF_CHANNEL_LAYOUT_2_2:
case CEF_CHANNEL_LAYOUT_QUAD:
case CEF_CHANNEL_LAYOUT_4_0:
return SPEAKERS_4POINT0; /**< Channels: FL, FR, FC, RC */
case CEF_CHANNEL_LAYOUT_4_1:
case CEF_CHANNEL_LAYOUT_5_0:
case CEF_CHANNEL_LAYOUT_5_0_BACK:
return SPEAKERS_4POINT1; /**< Channels: FL, FR, FC, LFE, RC */
case CEF_CHANNEL_LAYOUT_5_1:
case CEF_CHANNEL_LAYOUT_5_1_BACK:
return SPEAKERS_5POINT1; /**< Channels: FL, FR, FC, LFE, RL, RR */
case CEF_CHANNEL_LAYOUT_7_1:
case CEF_CHANNEL_LAYOUT_7_1_WIDE_BACK:
case CEF_CHANNEL_LAYOUT_7_1_WIDE:
return SPEAKERS_7POINT1; /**< Channels: FL, FR, FC, LFE, RL, RR, SL, SR */
default:
return SPEAKERS_UNKNOWN;
}
}
void BrowserClient::OnAudioStreamStarted(CefRefPtr<CefBrowser> browser, const CefAudioParameters ¶ms_,
int channels_)
{
UNUSED_PARAMETER(browser);
channels = channels_;
channel_layout = (ChannelLayout)params_.channel_layout;
sample_rate = params_.sample_rate;
frames_per_buffer = params_.frames_per_buffer;
}
void BrowserClient::OnAudioStreamPacket(CefRefPtr<CefBrowser> browser, const float **data, int frames, int64_t pts)
{
UNUSED_PARAMETER(browser);
if (!valid()) {
return;
}
struct obs_source_audio audio = {};
const uint8_t **pcm = (const uint8_t **)data;
speaker_layout speakers = GetSpeakerLayout(channel_layout);
int speaker_count = get_audio_channels(speakers);
for (int i = 0; i < speaker_count; i++)
audio.data[i] = pcm[i];
audio.samples_per_sec = sample_rate;
audio.frames = frames;
audio.format = AUDIO_FORMAT_FLOAT_PLANAR;
audio.speakers = speakers;
audio.timestamp = (uint64_t)pts * 1000000LLU;
obs_source_output_audio(bs->source, &audio);
}
void BrowserClient::OnAudioStreamStopped(CefRefPtr<CefBrowser> browser)
{
UNUSED_PARAMETER(browser);
}
void BrowserClient::OnAudioStreamError(CefRefPtr<CefBrowser> browser, const CefString &message)
{
UNUSED_PARAMETER(browser);
UNUSED_PARAMETER(message);
}
static CefAudioHandler::ChannelLayout Convert2CEFSpeakerLayout(int channels)
{
switch (channels) {
case 1:
return CEF_CHANNEL_LAYOUT_MONO;
case 2:
return CEF_CHANNEL_LAYOUT_STEREO;
case 3:
return CEF_CHANNEL_LAYOUT_2_1;
case 4:
return CEF_CHANNEL_LAYOUT_4_0;
case 5:
return CEF_CHANNEL_LAYOUT_4_1;
case 6:
return CEF_CHANNEL_LAYOUT_5_1;
case 8:
return CEF_CHANNEL_LAYOUT_7_1;
default:
return CEF_CHANNEL_LAYOUT_UNSUPPORTED;
}
}
bool BrowserClient::GetAudioParameters(CefRefPtr<CefBrowser> browser, CefAudioParameters ¶ms)
{
UNUSED_PARAMETER(browser);
int channels = (int)audio_output_get_channels(obs_get_audio());
params.channel_layout = Convert2CEFSpeakerLayout(channels);
params.sample_rate = (int)audio_output_get_sample_rate(obs_get_audio());
params.frames_per_buffer = kFramesPerBuffer;
return true;
}
void BrowserClient::OnLoadEnd(CefRefPtr<CefBrowser>, CefRefPtr<CefFrame> frame, int)
{
if (!valid()) {
return;
}
if (frame->IsMain() && bs->css.length()) {
std::string uriEncodedCSS = CefURIEncode(bs->css, false).ToString();
std::string script;
script += "const obsCSS = document.createElement('style');";
script += "obsCSS.appendChild(document.createTextNode("
"decodeURIComponent(\"" +
uriEncodedCSS + "\")));";
script += "document.querySelector('head').appendChild(obsCSS);";
frame->ExecuteJavaScript(script, "", 0);
}
}
bool BrowserClient::OnConsoleMessage(CefRefPtr<CefBrowser>, cef_log_severity_t level, const CefString &message,
const CefString &source, int line)
{
int errorLevel = LOG_INFO;
const char *code = "Info";
switch (level) {
case LOGSEVERITY_ERROR:
errorLevel = LOG_WARNING;
code = "Error";
break;
case LOGSEVERITY_FATAL:
errorLevel = LOG_ERROR;
code = "Fatal";
break;
default:
return false;
}
const char *sourceName = "<unknown>";
if (bs && bs->source)
sourceName = obs_source_get_name(bs->source);
blog(errorLevel, "[obs-browser: '%s'] %s: %s (%s:%d)", sourceName, code, message.ToString().c_str(),
source.ToString().c_str(), line);
return false;
}
07070100000013000081A400000000000000000000000168D5836B000017C4000000000000000000000000000000000000004800000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/browser-client.hpp/******************************************************************************
Copyright (C) 2014 by John R. Bradley <jrb@turrettech.com>
Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
#pragma once
#include <graphics/graphics.h>
#include <util/threading.h>
#include "cef-headers.hpp"
#include "obs-browser-source.hpp"
struct BrowserSource;
class BrowserClient : public CefClient,
public CefDisplayHandler,
public CefLifeSpanHandler,
public CefRequestHandler,
public CefResourceRequestHandler,
public CefContextMenuHandler,
public CefRenderHandler,
public CefAudioHandler,
public CefLoadHandler {
bool sharing_available = false;
bool reroute_audio = true;
ControlLevel webpage_control_level = DEFAULT_CONTROL_LEVEL;
inline bool valid() const;
void UpdateExtraTexture();
public:
BrowserSource *bs;
CefRect popupRect;
CefRect originalPopupRect;
int sample_rate;
int channels;
ChannelLayout channel_layout;
int frames_per_buffer;
inline BrowserClient(BrowserSource *bs_, bool sharing_avail, bool reroute_audio_,
ControlLevel webpage_control_level_)
: sharing_available(sharing_avail),
reroute_audio(reroute_audio_),
webpage_control_level(webpage_control_level_),
bs(bs_)
{
}
/* CefClient */
virtual CefRefPtr<CefLoadHandler> GetLoadHandler() override;
virtual CefRefPtr<CefRenderHandler> GetRenderHandler() override;
virtual CefRefPtr<CefDisplayHandler> GetDisplayHandler() override;
virtual CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() override;
virtual CefRefPtr<CefRequestHandler> GetRequestHandler() override;
virtual CefRefPtr<CefContextMenuHandler> GetContextMenuHandler() override;
virtual CefRefPtr<CefAudioHandler> GetAudioHandler() override;
virtual bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame,
CefProcessId source_process,
CefRefPtr<CefProcessMessage> message) override;
/* CefDisplayHandler */
virtual bool OnConsoleMessage(CefRefPtr<CefBrowser> browser, cef_log_severity_t level, const CefString &message,
const CefString &source, int line) override;
virtual bool OnTooltip(CefRefPtr<CefBrowser> browser, CefString &text) override;
/* CefLifeSpanHandler */
virtual bool OnBeforePopup(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame,
#if CHROME_VERSION_BUILD >= 6834
int popup_id,
#endif
const CefString &target_url, const CefString &target_frame_name,
cef_window_open_disposition_t target_disposition, bool user_gesture,
const CefPopupFeatures &popupFeatures, CefWindowInfo &windowInfo,
CefRefPtr<CefClient> &client, CefBrowserSettings &settings,
CefRefPtr<CefDictionaryValue> &extra_info, bool *no_javascript_access) override;
/* CefRequestHandler */
virtual CefRefPtr<CefResourceRequestHandler>
GetResourceRequestHandler(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request, bool is_navigation, bool is_download,
const CefString &request_initiator, bool &disable_default_handling) override;
virtual void OnRenderProcessTerminated(CefRefPtr<CefBrowser> browser, TerminationStatus status
#if CHROME_VERSION_BUILD >= 6367
,
int error_code, const CefString &error_string
#endif
) override;
/* CefResourceRequestHandler */
virtual CefResourceRequestHandler::ReturnValue OnBeforeResourceLoad(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
CefRefPtr<CefCallback> callback) override;
/* CefContextMenuHandler */
virtual void OnBeforeContextMenu(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame,
CefRefPtr<CefContextMenuParams> params,
CefRefPtr<CefMenuModel> model) override;
/* CefRenderHandler */
virtual void GetViewRect(CefRefPtr<CefBrowser> browser, CefRect &rect) override;
virtual void OnPaint(CefRefPtr<CefBrowser> browser, PaintElementType type, const RectList &dirtyRects,
const void *buffer, int width, int height) override;
#ifdef ENABLE_BROWSER_SHARED_TEXTURE
virtual void OnAcceleratedPaint(CefRefPtr<CefBrowser> browser, PaintElementType type,
const RectList &dirtyRects,
#if CHROME_VERSION_BUILD >= 6367
const CefAcceleratedPaintInfo &info) override;
#else
void *shared_handle) override;
#endif
#ifdef CEF_ON_ACCELERATED_PAINT2
virtual void OnAcceleratedPaint2(CefRefPtr<CefBrowser> browser, PaintElementType type,
const RectList &dirtyRects, void *shared_handle, bool new_texture) override;
#endif
#endif
virtual void OnAudioStreamPacket(CefRefPtr<CefBrowser> browser, const float **data, int frames,
int64_t pts) override;
virtual void OnAudioStreamStopped(CefRefPtr<CefBrowser> browser) override;
virtual void OnAudioStreamStarted(CefRefPtr<CefBrowser> browser, const CefAudioParameters ¶ms,
int channels) override;
virtual void OnAudioStreamError(CefRefPtr<CefBrowser> browser, const CefString &message) override;
const int kFramesPerBuffer = 1024;
virtual bool GetAudioParameters(CefRefPtr<CefBrowser> browser, CefAudioParameters ¶ms) override;
/* CefLoadHandler */
virtual void OnLoadEnd(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, int httpStatusCode) override;
IMPLEMENT_REFCOUNTING(BrowserClient);
};
07070100000014000081A400000000000000000000000168D5836B000000E4000000000000000000000000000000000000004900000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/browser-config.h.in#pragma once
#ifndef ON
#define ON true
#endif
#ifndef OFF
#define OFF false
#endif
#ifndef TRUE
#define TRUE true
#endif
#ifndef FALSE
#define FALSE false
#endif
#ifdef __APPLE__
#define CEF_LIBRARY "@CEF_LIBRARY@"
#endif
07070100000015000081A400000000000000000000000168D5836B00000857000000000000000000000000000000000000004800000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/browser-scheme.cpp/******************************************************************************
Copyright (C) 2014 by John R. Bradley <jrb@turrettech.com>
Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
#include "browser-scheme.hpp"
#include "wide-string.hpp"
#include <include/wrapper/cef_stream_resource_handler.h>
CefRefPtr<CefResourceHandler> BrowserSchemeHandlerFactory::Create(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame>,
const CefString &, CefRefPtr<CefRequest> request)
{
if (!browser || !request)
return nullptr;
CefURLParts parts;
CefParseURL(request->GetURL(), parts);
std::string path = CefString(&parts.path);
path = CefURIDecode(path, true, cef_uri_unescape_rule_t::UU_SPACES);
path = CefURIDecode(path, true, cef_uri_unescape_rule_t::UU_URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS);
std::string fileExtension = path.substr(path.find_last_of(".") + 1);
for (char &ch : fileExtension)
ch = (char)tolower(ch);
if (fileExtension.compare("woff2") == 0)
fileExtension = "woff";
#ifdef _WIN32
CefRefPtr<CefStreamReader> stream = CefStreamReader::CreateForFile(path.substr(1));
#else
CefRefPtr<CefStreamReader> stream = CefStreamReader::CreateForFile(path);
#endif
if (stream) {
CefString mimeType = CefGetMimeType(fileExtension);
if (mimeType.empty())
mimeType = "application/octet-stream";
return new CefStreamResourceHandler(mimeType, stream);
} else {
return nullptr;
}
}
07070100000016000081A400000000000000000000000168D5836B00000504000000000000000000000000000000000000004800000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/browser-scheme.hpp/******************************************************************************
Copyright (C) 2014 by John R. Bradley <jrb@turrettech.com>
Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
#pragma once
#include "cef-headers.hpp"
#include <string>
#include <fstream>
class BrowserSchemeHandlerFactory : public CefSchemeHandlerFactory {
public:
virtual CefRefPtr<CefResourceHandler> Create(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame>,
const CefString &, CefRefPtr<CefRequest> request) override;
IMPLEMENT_REFCOUNTING(BrowserSchemeHandlerFactory);
};
07070100000017000081A400000000000000000000000168D5836B000002C3000000000000000000000000000000000000004700000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/browser-version.h#pragma once
#define OBS_BROWSER_VERSION_MAJOR 2
#define OBS_BROWSER_VERSION_MINOR 26
#define OBS_BROWSER_VERSION_PATCH 2
#ifndef MAKE_SEMANTIC_VERSION
#define MAKE_SEMANTIC_VERSION(major, minor, patch) ((major << 24) | (minor << 16) | patch)
#endif
#define OBS_BROWSER_VERSION_INT \
MAKE_SEMANTIC_VERSION(OBS_BROWSER_VERSION_MAJOR, OBS_BROWSER_VERSION_MINOR, OBS_BROWSER_VERSION_PATCH)
#define OBS_BROWSER_MACRO_STR_(x) #x
#define OBS_BROWSER_MACRO_STR(x) OBS_BROWSER_MACRO_STR_(x)
#define OBS_BROWSER_VERSION_STRING \
OBS_BROWSER_MACRO_STR(OBS_BROWSER_VERSION_MAJOR) \
"." OBS_BROWSER_MACRO_STR(OBS_BROWSER_VERSION_MINOR) "." OBS_BROWSER_MACRO_STR(OBS_BROWSER_VERSION_PATCH)
07070100000018000041ED00000000000000000000000268D5836B00000000000000000000000000000000000000000000003F00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/build-aux07070100000019000041ED00000000000000000000000268D5836B00000000000000000000000000000000000000000000004A00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/build-aux/.functions0707010000001A000081A400000000000000000000000168D5836B0000007F000000000000000000000000000000000000005400000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/build-aux/.functions/log_debugif (( ! ${+_loglevel} )) typeset -g _loglevel=1
if (( _loglevel > 2 )) print -PR -e -- "${CI:+::debug::}%F{220}DEBUG: ${@}%f"
0707010000001B000081A400000000000000000000000168D5836B0000004E000000000000000000000000000000000000005400000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/build-aux/.functions/log_errorlocal icon=' ✖︎ '
print -u2 -PR "${CI:+::error::}%F{1} ${icon} %f ${@}"
0707010000001C000081A400000000000000000000000168D5836B0000011C000000000000000000000000000000000000005400000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/build-aux/.functions/log_groupautoload -Uz log_info
if (( ! ${+_log_group} )) typeset -g _log_group=0
if (( ${+CI} )) {
if (( _log_group )) {
print "::endgroup::"
typeset -g _log_group=0
}
if (( # )) {
print "::group::${@}"
typeset -g _log_group=1
}
} else {
if (( # )) log_info ${@}
}
0707010000001D000081A400000000000000000000000168D5836B0000008E000000000000000000000000000000000000005300000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/build-aux/.functions/log_infoif (( ! ${+_loglevel} )) typeset -g _loglevel=1
if (( _loglevel > 0 )) {
local icon=' =>'
print -PR "%F{4} ${(r:5:)icon}%f %B${@}%b"
}
0707010000001E000081A400000000000000000000000168D5836B00000080000000000000000000000000000000000000005500000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/build-aux/.functions/log_outputif (( ! ${+_loglevel} )) typeset -g _loglevel=1
if (( _loglevel > 0 )) {
local icon=''
print -PR " ${(r:5:)icon} ${@}"
}
0707010000001F000081A400000000000000000000000168D5836B0000008A000000000000000000000000000000000000005500000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/build-aux/.functions/log_statusif (( ! ${+_loglevel} )) typeset -g _loglevel=1
if (( _loglevel > 0 )) {
local icon=' >'
print -PR "%F{2} ${(r:5:)icon}%f ${@}"
}
07070100000020000081A400000000000000000000000168D5836B0000009C000000000000000000000000000000000000005600000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/build-aux/.functions/log_warningif (( ! ${+_loglevel} )) typeset -g _loglevel=1
if (( _loglevel > 0 )) {
local icon=' =>'
print -PR "${CI:+::warning::}%F{3} ${(r:5:)icon} ${@}%f"
}
07070100000021000081A400000000000000000000000168D5836B000001AD000000000000000000000000000000000000005700000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/build-aux/.functions/set_loglevelautoload -Uz log_debug log_error
local -r _usage="Usage: %B${0}%b <loglevel>
Set log level, following levels are supported: 0 (quiet), 1 (normal), 2 (verbose), 3 (debug)"
if (( ! # )); then
log_error 'Called without arguments.'
log_output ${_usage}
return 2
elif (( ${1} >= 4 )); then
log_error 'Called with loglevel > 3.'
log_output ${_usage}
fi
typeset -g -i -r _loglevel=${1}
log_debug "Log level set to '${1}'"
07070100000022000081ED00000000000000000000000168D5836B000020B3000000000000000000000000000000000000004F00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/build-aux/.run-format.zsh#!/usr/bin/env zsh
builtin emulate -L zsh
setopt EXTENDED_GLOB
setopt PUSHD_SILENT
setopt ERR_EXIT
setopt ERR_RETURN
setopt NO_UNSET
setopt PIPE_FAIL
setopt NO_AUTO_PUSHD
setopt NO_PUSHD_IGNORE_DUPS
setopt FUNCTION_ARGZERO
## Enable for script debugging
# setopt WARN_CREATE_GLOBAL
# setopt WARN_NESTED_VAR
# setopt XTRACE
autoload -Uz is-at-least && if ! is-at-least 5.2; then
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."
exit 1
fi
invoke_formatter() {
if (( # < 1 )) {
log_error "Usage invoke_formatter [formatter_name]"
exit 2
}
local formatter="${1}"
shift
local -a source_files=(${@})
case ${formatter} {
clang)
if (( ${+commands[clang-format-19]} )) {
local formatter=clang-format-19
} elif (( ${+commands[clang-format]} )) {
local formatter=clang-format
} else {
log_error "No viable clang-format version found (required 19.1.1)"
exit 2
}
local -a formatter_version=($(${formatter} --version))
if ! is-at-least 19.1.1 ${formatter_version[-1]}; then
log_error "clang-format is not version 19.1.1 or above (found ${formatter_version[-1]}."
exit 2
fi
if ! is-at-least ${formatter_version[-1]} 19.1.1; then
log_error "clang-format is more recent than version 19.1.1 (found ${formatter_version[-1]})."
exit 2
fi
if (( ! #source_files )) source_files=((libobs|libobs-*|frontend|plugins|deps|shared|test)/**/*.(c|cpp|h|hpp|m|mm)(.N))
source_files=(${source_files:#*/(obs-websocket/deps|decklink/*/decklink-sdk|mac-syphon/syphon-framework|libdshowcapture)/*})
local -a format_args=(-style=file -fallback-style=none)
if (( _loglevel > 2 )) format_args+=(--verbose)
check_files() {
local -i num_failures=0
local -a source_files=($@)
local file
local -a format_args=(-style=file -fallback-style=none)
if (( _loglevel > 2 )) format_args+=(--verbose)
local -a command=(${formatter} ${format_args})
for file (${source_files}) {
if ! ${command} "${file}" | diff -q "${file}" - &> /dev/null; then
log_error "${file} requires formatting changes."
if (( fail_on_error == 2 )) return 2;
num_failures=$(( num_failures + 1 ))
fi
}
if (( num_failures && fail_on_error == 1 )) return 2
}
format_files() {
local -a source_files=($@)
if (( ${#source_files} )) {
local -a format_args=(-style=file -fallback-style=none -i)
if (( _loglevel > 2 )) format_args+=(--verbose)
"${formatter}" ${format_args} ${source_files}
}
}
;;
gersemi)
local formatter=gersemi
if (( ${+commands[gersemi]} )) {
local gersemi_version=($(gersemi --version))
if ! is-at-least 0.21.0 ${gersemi_version[2]}; then
log_error "gersemi is not version 0.21.0 or above (found ${gersemi_version[2]}."
exit 2
fi
}
if (( ! #source_files )) source_files=(CMakeLists.txt (libobs|libobs-*|frontend|plugins|deps|shared|cmake|test)/**/(CMakeLists.txt|*.cmake)(.N))
source_files=(${source_files:#*/(jansson|decklink/*/decklink-sdk|obs-websocket|obs-browser|libdshowcapture)/*})
source_files=(${source_files:#(cmake/Modules/*|*/legacy.cmake)})
check_files() {
local -i num_failures=0
local -a source_files=($@)
local file
local -a command=(${formatter} -c --no-cache ${source_files})
if (( ${#source_files} )) {
while read -r line; do
local -a line_tokens=(${(z)line})
if (( #line_tokens )) {
file=${line_tokens[1]//*${project_root}\//}
log_error "${file} requires formatting changes."
} else {
log_error "${line}"
}
if (( fail_on_error == 2 )) return 2
num_failures=$(( num_failures + 1 ))
done < <(${command} 2>&1)
if (( num_failures && fail_on_error == 1 )) return 2
}
}
format_files() {
local -a source_files=($@)
if (( ${#source_files} )) {
"${formatter}" -i ${source_files}
}
}
;;
swift)
local formatter=swift-format
if (( ${+commands[swift-format]} )) {
local swift_format_version=$(swift-format --version)
if ! is-at-least 508.0.0 ${swift_format_version}; then
log_error "swift-format is not version 508.0.0 or above (found ${swift_format_version})."
exit 2
fi
} else {
log_error "No viable swift-format version found (required 508.0.0)"
exit 2
}
if (( ! #source_files )) source_files=((libobs|libobs-*|frontend|plugins)/**/*.swift(.N))
check_files() {
local -i num_failures=0
local -a source_files=($@)
local file
local -a format_args=()
local -a command=(${formatter} ${format_args})
for file (${source_files}) {
if ! "${command}" "${file}" | diff -q "${file}" - &> /dev/null; then
log_error "${file} requires formatting changes."
if (( fail_on_error == 2 )) return 2;
num_failures=$(( num_failures + 1 ))
fi
}
if (( num_failures && fail_on_error == 1 )) return 2
}
format_files() {
local -a source_files=($@)
if (( ${#source_files} )) {
local -a format_args=(-i)
"${formatter}" ${format_args} ${source_files}
}
}
;;
*) log_error "Invalid formatter specified: ${1}. Valid options are clang-format, gersemi, and swift-format."; exit 2 ;;
}
local file
local -i num_failures=0
if (( check_only )) {
if (( ${+functions[check_files]} )) {
check_files ${source_files}
} else {
log_error "No format check function defined for formatter '${formatter}'"
exit 2
}
} else {
if (( ${+functions[format_files]} )) {
format_files ${source_files}
} else {
log_error "No format function defined for formatter '${formatter}'"
exit 2
}
}
}
run_format() {
if (( ! ${+SCRIPT_HOME} )) typeset -g SCRIPT_HOME=${ZSH_ARGZERO:A:h}
if (( ! ${+FORMATTER_NAME} )) typeset -g FORMATTER_NAME=${${(s:-:)ZSH_ARGZERO:t:r}[2]}
local project_root=${SCRIPT_HOME:A:h}
typeset -g host_os=${${(L)$(uname -s)}//darwin/macos}
local -i fail_on_error=0
local -i check_only=0
local -i verbosity=1
local -r _version='1.0.0'
fpath=("${SCRIPT_HOME}/.functions" ${fpath})
autoload -Uz set_loglevel log_info log_error log_output log_status log_warning
local -r _usage="
Usage: %B${functrace[1]%:*}%b <option>
%BOptions%b:
%F{yellow} Formatting options%f
-----------------------------------------------------------------------------
%B-c | --check%b Check only, no actual formatting takes place
%F{yellow} Output options%f
-----------------------------------------------------------------------------
%B-v | --verbose%b Verbose (more detailed output)
%B--fail-[never|error] Fail script never/on formatting change - default: %B%F{green}never%f%b
%B--debug%b Debug (very detailed and added output)
%F{yellow} General options%f
-----------------------------------------------------------------------------
%B-h | --help%b Print this usage help
%B-V | --version%b Print script version information"
local -a args
while (( # )) {
case ${1} {
-c|--check) check_only=1; shift ;;
-v|--verbose) (( verbosity += 1 )); shift ;;
-h|--help) log_output ${_usage}; exit 0 ;;
-V|--version) print -Pr "${_version}"; exit 0 ;;
--debug) verbosity=3; shift ;;
--fail-never)
fail_on_error=0
shift
;;
--fail-error)
fail_on_error=1
shift
;;
--fail-fast)
fail_on_error=2
shift
;;
*)
args+=($@)
break
;;
}
}
set -- ${(@)args}
set_loglevel ${verbosity}
invoke_formatter ${FORMATTER_NAME} ${args}
}
run_format ${@}
070701000000230000A1FF00000000000000000000000168D5836B0000000F000000000000000000000000000000000000005000000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/build-aux/run-clang-format.run-format.zsh07070100000024000081A400000000000000000000000168D5836B0000087A000000000000000000000000000000000000004500000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/cef-headers.hpp/******************************************************************************
Copyright (C) 2014 by John R. Bradley <jrb@turrettech.com>
Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
#pragma once
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4100 4996)
#else
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
#include <include/cef_app.h>
#include <include/cef_base.h>
#include <include/cef_task.h>
#include <include/cef_client.h>
#include <include/cef_parser.h>
#include <include/cef_scheme.h>
#include <include/cef_version.h>
#if CHROME_VERSION_BUILD >= 6943
#include <include/cef_version_info.h>
#endif
#include <include/cef_render_process_handler.h>
#include <include/cef_request_context_handler.h>
#include <include/cef_jsdialog_handler.h>
#if defined(__APPLE__)
#include "include/wrapper/cef_library_loader.h"
#endif
#if !defined(_WIN32) && !defined(__APPLE__) && \
(CHROME_VERSION_BUILD >= 6943 || (CHROME_VERSION_BUILD > 6337 && defined(CEF_OSR_EXTRA_INFO)))
#define ENABLE_BROWSER_SHARED_TEXTURE
#endif
#define SendBrowserProcessMessage(browser, pid, msg) \
CefRefPtr<CefFrame> mainFrame = browser->GetMainFrame(); \
if (mainFrame) { \
mainFrame->SendProcessMessage(pid, msg); \
}
#ifdef _MSC_VER
#pragma warning(pop)
#else
#pragma GCC diagnostic pop
#endif
07070100000025000041ED00000000000000000000000268D5836B00000000000000000000000000000000000000000000003B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/cmake07070100000026000081A400000000000000000000000168D5836B00000347000000000000000000000000000000000000005000000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/cmake/feature-panels.cmakefind_package(Qt6 REQUIRED Widgets)
add_library(browser-panels INTERFACE)
add_library(OBS::browser-panels ALIAS browser-panels)
target_sources(browser-panels INTERFACE panel/browser-panel.hpp)
target_include_directories(browser-panels INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/panel")
target_compile_definitions(browser-panels INTERFACE BROWSER_AVAILABLE)
target_sources(
obs-browser
PRIVATE # cmake-format: sortable
panel/browser-panel-client.cpp panel/browser-panel-client.hpp panel/browser-panel-internal.hpp
panel/browser-panel.cpp)
target_link_libraries(obs-browser PRIVATE OBS::browser-panels Qt::Widgets)
set_target_properties(
obs-browser
PROPERTIES AUTOMOC ON
AUTOUIC ON
AUTORCC ON)
if(OS_WINDOWS)
set_property(SOURCE browser-app.hpp PROPERTY SKIP_AUTOMOC TRUE)
endif()
07070100000027000041ED00000000000000000000000268D5836B00000000000000000000000000000000000000000000004100000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/cmake/macos07070100000028000081A400000000000000000000000168D5836B0000050B000000000000000000000000000000000000005600000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/cmake/macos/Info-helper.plist.in<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDisplayName</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>com.obsproject.obs-studio.helper${BUNDLE_ID_SUFFIX}</string>
<key>CFBundleVersion</key>
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
<key>CFBundleShortVersionString</key>
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>LSEnvironment</key>
<dict>
<key>MallocNanoZone</key>
<string>0</string>
</dict>
<key>LSFileQuarantineEnabled</key>
<true/>
<key>LSMinimumSystemVersion</key>
<string>${CMAKE_OSX_DEPLOYMENT_TARGET}</string>
<key>LSUIElement</key>
<string>1</string>
<key>NSSupportsAutomaticGraphicsSwitching</key>
<true/>
<key>NSHumanReadableCopyright</key>
<string>(c) 2012-${CURRENT_YEAR} Lain Bailey</string>
</dict>
</plist>
07070100000029000081A400000000000000000000000168D5836B000003AD000000000000000000000000000000000000004F00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/cmake/macos/Info.plist.in<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleName</key>
<string>obs-browser</string>
<key>CFBundleIdentifier</key>
<string>com.obsproject.obs-browser</string>
<key>CFBundleVersion</key>
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
<key>CFBundleShortVersionString</key>
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleExecutable</key>
<string>obs-browser</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleSupportedPlatforms</key>
<array>
<string>MacOSX</string>
</array>
<key>LSMinimumSystemVersion</key>
<string>${CMAKE_OSX_DEPLOYMENT_TARGET}</string>
<key>NSHumanReadableCopyright</key>
<string>(c) 2012-${CURRENT_YEAR} Lain Bailey</string>
</dict>
</plist>
0707010000002A000081A400000000000000000000000168D5836B00000143000000000000000000000000000000000000005F00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/cmake/macos/entitlements-helper.gpu.plist<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<key>com.apple.security.cs.allow-jit</key>
<true/>
</dict>
</plist>
0707010000002B000081A400000000000000000000000168D5836B0000018F000000000000000000000000000000000000005B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/cmake/macos/entitlements-helper.plist<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<key>com.apple.security.cs.allow-jit</key>
<true/>
</dict>
</plist>
0707010000002C000081A400000000000000000000000168D5836B0000015A000000000000000000000000000000000000006200000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/cmake/macos/entitlements-helper.plugin.plist<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
</dict>
</plist>
0707010000002D000081A400000000000000000000000168D5836B00000143000000000000000000000000000000000000006400000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/cmake/macos/entitlements-helper.renderer.plist<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
</dict>
</plist>
0707010000002E000081A400000000000000000000000168D5836B0000052C000000000000000000000000000000000000004A00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/cmake/os-linux.cmakefind_package(X11 REQUIRED)
find_package(Libdrm REQUIRED)
get_target_property(libdrm_include_directories Libdrm::Libdrm INTERFACE_INCLUDE_DIRECTORIES)
target_include_directories(obs-browser PRIVATE ${libdrm_include_directories})
target_link_libraries(obs-browser PRIVATE CEF::Wrapper CEF::Library X11::X11)
set_target_properties(obs-browser PROPERTIES BUILD_RPATH "$ORIGIN/" INSTALL_RPATH "$ORIGIN/")
target_sources(obs-browser PRIVATE drm-format.cpp drm-format.hpp)
add_executable(browser-helper)
add_executable(OBS::browser-helper ALIAS browser-helper)
target_sources(
browser-helper PRIVATE # cmake-format: sortable
browser-app.cpp browser-app.hpp cef-headers.hpp obs-browser-page/obs-browser-page-main.cpp)
target_include_directories(browser-helper PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/deps"
"${CMAKE_CURRENT_SOURCE_DIR}/obs-browser-page")
target_link_libraries(browser-helper PRIVATE CEF::Wrapper CEF::Library)
set(OBS_EXECUTABLE_DESTINATION "${OBS_PLUGIN_DESTINATION}")
# cmake-format: off
set_target_properties_obs(
browser-helper
PROPERTIES FOLDER plugins/obs-browser
BUILD_RPATH "$ORIGIN/"
INSTALL_RPATH "$ORIGIN/"
PREFIX ""
OUTPUT_NAME obs-browser-page)
# cmake-format: on
0707010000002F000081A400000000000000000000000168D5836B00000A3E000000000000000000000000000000000000004A00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/cmake/os-macos.cmakefind_package(Qt6 REQUIRED Widgets)
target_compile_definitions(obs-browser PRIVATE ENABLE_BROWSER_SHARED_TEXTURE ENABLE_BROWSER_QT_LOOP)
if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 14.0.3)
target_compile_options(obs-browser PRIVATE -Wno-error=unqualified-std-cast-call)
endif()
target_link_libraries(obs-browser PRIVATE Qt::Widgets CEF::Wrapper "$<LINK_LIBRARY:FRAMEWORK,CoreFoundation.framework>"
"$<LINK_LIBRARY:FRAMEWORK,AppKit.framework>")
set(helper_basename browser-helper)
set(helper_output_name "OBS Helper")
set(helper_suffixes "::" " (GPU):_gpu:.gpu" " (Plugin):_plugin:.plugin" " (Renderer):_renderer:.renderer")
foreach(helper IN LISTS helper_suffixes)
string(REPLACE ":" ";" helper ${helper})
list(GET helper 0 helper_name)
list(GET helper 1 helper_target)
list(GET helper 2 helper_plist)
set(target_name ${helper_basename}${helper_target})
set(target_output_name "${helper_output_name}${helper_name}")
set(EXECUTABLE_NAME "${target_output_name}")
set(BUNDLE_ID_SUFFIX ${helper_plist})
configure_file(cmake/macos/Info-helper.plist.in Info-Helper${helper_plist}.plist)
add_executable(${target_name} MACOSX_BUNDLE EXCLUDE_FROM_ALL)
add_executable(OBS::${target_name} ALIAS ${target_name})
target_sources(
${target_name} PRIVATE # cmake-format: sortable
browser-app.cpp browser-app.hpp cef-headers.hpp obs-browser-page/obs-browser-page-main.cpp)
target_compile_definitions(${target_name} PRIVATE ENABLE_BROWSER_SHARED_TEXTURE)
if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 14.0.3)
target_compile_options(${target_name} PRIVATE -Wno-error=unqualified-std-cast-call)
endif()
target_include_directories(${target_name} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/deps"
"${CMAKE_CURRENT_SOURCE_DIR}/obs-browser-page")
target_link_libraries(${target_name} PRIVATE CEF::Wrapper nlohmann_json::nlohmann_json)
set_target_properties(
${target_name}
PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_BINARY_DIR}/Info-Helper${helper_plist}.plist"
OUTPUT_NAME "${target_output_name}"
FOLDER plugins/obs-browser/Helpers
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER com.obsproject.obs-studio.helper${helper_plist}
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/entitlements-helper${helper_plist}.plist")
endforeach()
set_target_properties(
obs-browser
PROPERTIES AUTOMOC ON
AUTOUIC ON
AUTORCC ON)
07070100000030000081A400000000000000000000000168D5836B00000564000000000000000000000000000000000000004C00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/cmake/os-windows.cmaketarget_compile_options(obs-browser PRIVATE $<IF:$<CONFIG:DEBUG>,/MTd,/MT>)
target_compile_definitions(obs-browser PRIVATE ENABLE_BROWSER_SHARED_TEXTURE)
target_link_libraries(obs-browser PRIVATE CEF::Wrapper CEF::Library d3d11 dxgi)
target_link_options(obs-browser PRIVATE /IGNORE:4099)
add_executable(obs-browser-helper WIN32 EXCLUDE_FROM_ALL)
add_executable(OBS::browser-helper ALIAS obs-browser-helper)
target_sources(
obs-browser-helper
PRIVATE # cmake-format: sortable
browser-app.cpp browser-app.hpp cef-headers.hpp obs-browser-page.manifest
obs-browser-page/obs-browser-page-main.cpp)
target_include_directories(obs-browser-helper PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/deps"
"${CMAKE_CURRENT_SOURCE_DIR}/obs-browser-page")
target_compile_options(obs-browser-helper PRIVATE $<IF:$<CONFIG:DEBUG>,/MTd,/MT>)
target_compile_definitions(obs-browser-helper PRIVATE ENABLE_BROWSER_SHARED_TEXTURE)
target_link_libraries(obs-browser-helper PRIVATE CEF::Wrapper CEF::Library nlohmann_json::nlohmann_json)
target_link_options(obs-browser-helper PRIVATE /IGNORE:4099 /SUBSYSTEM:WINDOWS)
set(OBS_EXECUTABLE_DESTINATION "${OBS_PLUGIN_DESTINATION}")
set_target_properties_obs(
obs-browser-helper
PROPERTIES FOLDER plugins/obs-browser
PREFIX ""
OUTPUT_NAME obs-browser-page)
07070100000031000041ED00000000000000000000000268D5836B00000000000000000000000000000000000000000000004300000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/cmake/windows07070100000032000081A400000000000000000000000168D5836B00000332000000000000000000000000000000000000005D00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/cmake/windows/obs-browser-page.manifest<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<description>OBS Browser Page (CEF)</description>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel
level="asInvoker"
uiAccess="false"
/>
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<maxversiontested Id="10.0.22000.0"/>
<!-- Windows 10 and Windows 11 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
</application>
</compatibility>
</assembly>
07070100000033000041ED00000000000000000000000268D5836B00000000000000000000000000000000000000000000003A00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data07070100000034000081A400000000000000000000000168D5836B000027B5000000000000000000000000000000000000004500000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/error.html<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<title>OBS | Browser Panel</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<meta name="description" content="OBS (Open Broadcaster Software) is free and open source software for video recording and live streaming. Stream to Twitch, YouTube and many other providers or record your own videos with high quality H264 / AAC encoding.">
<meta name="keywords" content="OBS,OBS Studio,Stream,Video,Live Streaming,Recording,Games,Twitch,YouTube,Livestream,Open Broadcaster Software">
<meta name="obs-width" content="800">
<meta name="obs-height" content="600">
<meta name="obs-fps" content="60">
<meta name="obs-shutdown" content="true">
<meta name="obs-refresh" content="false">
<meta name="obs-browser" content="width=800,height=600,fps=60,shutdown=true,refresh=false">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="96x96" href="/favicon-96x96.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<style>
/* Style reset */
/* http://meyerweb.com/eric/tools/css/reset/
v2.0 | 20110126
License: none (public domain)
*/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
/* End of style reset */
body {
font-family: "Open Sans", sans-serif;
color: white;
}
p + h1, p + h2, p + h3, p + h4, p + h5 {
margin-top: 32px;
}
p + p {
margin-top: 24px;
}
.main_slant {
position: absolute;
width: 200%;
left: -50%;
}
#bg_slant1 {
left: -80%;
bottom: -20%;
height: 75%;
width: 250%;
background: #202d6f;
transform: rotate(15deg);
}
#bg_slant2 {
height: 200px;
background: #1c2b71;
transform: rotate(1deg);
top: -50px;
}
#bg_slant3 {
height: 500px;
background: #0C1633;
transform: rotate(-8deg);
bottom: -200px;
}
.bs-page {
background: #162458;
font-size: 14px;
position: relative;
line-height: 30px;
text-align: center;
padding: 2%;
max-width: 100vw;
min-height: 100vh;
box-sizing: border-box;
overflow: hidden;
}
.bs-container {
position: relative;
max-width: 800px;
margin: 0px auto;
}
.bs-image,
#Layer_1 {
display: block;
width: 80px;
margin: 20px auto 30px;
}
.bold {
font-weight: bold;
}
#error-url {
overflow-wrap: break-word;
}
#retry-link {
color: lightgrey;
}
#retry-link:hover {
color: white;
}
</style>
</head>
<body>
<div id="wrapper">
<div class="bs-page">
<div id="bg_slant1" class="main_slant"></div>
<div id="bg_slant2" class="main_slant"></div>
<div id="bg_slant3" class="main_slant"></div>
<div class="bs-container">
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 993 993"><defs><style>.cls-1{fill:url(#radial-gradient);}.cls-2{fill:none;}.cls-3{fill:url(#radial-gradient-2);}.cls-4{fill:url(#radial-gradient-3);}.cls-5{clip-path:url(#clip-path);}.cls-6{fill:url(#radial-gradient-5);}.cls-7{clip-path:url(#clip-path-2);}.cls-8{fill:url(#radial-gradient-6);}.cls-9{fill:url(#radial-gradient-7);}</style><radialGradient id="radial-gradient" cx="512.06144" cy="512.12118" r="444.12235" gradientUnits="userSpaceOnUse"><stop offset="0.99" stop-opacity="0.5"/><stop offset="1" stop-opacity="0"/></radialGradient><radialGradient id="radial-gradient-2" cx="514.5" cy="516.5" r="496.5" gradientUnits="userSpaceOnUse"><stop offset="0.91341"/><stop offset="1" stop-opacity="0"/></radialGradient><radialGradient id="radial-gradient-3" cx="512.05911" cy="512.1212" r="486.27268" gradientUnits="userSpaceOnUse"><stop offset="0.99" stop-color="#fff"/><stop offset="0.99505" stop-color="#fdfdfd"/><stop offset="0.99687" stop-color="#f6f6f6"/><stop offset="0.99817" stop-color="#ebebeb"/><stop offset="0.99921" stop-color="#dadada"/><stop offset="1" stop-color="#c7c7c7"/></radialGradient><clipPath id="clip-path" transform="translate(-18 -20)"><path id="SVGID" class="cls-1" d="M67.93793,512.1212c0,245.28622,198.83963,444.12116,444.12116,444.12116S956.185,757.40742,956.185,512.1212C956.185,266.83966,757.34063,68,512.05909,68S67.93793,266.83966,67.93793,512.1212"/></clipPath><radialGradient id="radial-gradient-5" cx="494.0614" cy="492.1212" r="444.12237" xlink:href="#radial-gradient"/><clipPath id="clip-path-2" transform="translate(-18 -20)"><path class="cls-2" d="M71.60309,512.1212c0,243.262,197.19869,440.456,440.456,440.456s440.46067-197.194,440.46067-440.456c0-243.25733-197.20334-440.45606-440.46067-440.45606S71.60309,268.86387,71.60309,512.1212"/></clipPath><radialGradient id="radial-gradient-6" cx="5.29522" cy="1029.31443" r="4.65333" gradientTransform="matrix(94.65428, 0, 0, -94.65428, -7.15418, 97921.13875)" gradientUnits="userSpaceOnUse"><stop offset="0"/><stop offset="1" stop-color="#322f32"/></radialGradient><radialGradient id="radial-gradient-7" cx="578.89023" cy="473.31944" r="353.94182" gradientTransform="translate(0 -45.94173) scale(1 1.09706)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#c2c0c2"/><stop offset="1" stop-color="#ebebeb"/></radialGradient></defs><title>OBS-Alt-2</title><path class="cls-3" d="M514.5,20C788.71048,20,1011,242.28952,1011,516.5S788.71048,1013,514.5,1013,18,790.71056,18,516.5,240.28946,20,514.5,20" transform="translate(-18 -20)"/><path class="cls-4" d="M512.05911,25.84852c268.56207,0,486.27268,217.71061,486.27268,486.27268S780.62118,998.39388,512.05911,998.39388,25.78643,780.68334,25.78643,512.1212,243.497,25.84852,512.05911,25.84852" transform="translate(-18 -20)"/><path id="SVGID-2" data-name="SVGID" class="cls-1" d="M67.93793,512.1212c0,245.28622,198.83963,444.12116,444.12116,444.12116S956.185,757.40742,956.185,512.1212C956.185,266.83966,757.34063,68,512.05909,68S67.93793,266.83966,67.93793,512.1212" transform="translate(-18 -20)"/><g class="cls-5"><rect class="cls-6" x="49.93786" y="48" width="888.24709" height="888.2424"/></g><g class="cls-7"><rect class="cls-8" x="53.60302" y="51.66514" width="880.91676" height="880.91211"/></g><path class="cls-9" d="M684.907,370.50177a199.24212,199.24212,0,0,1-277.2406,70.45605A202.74787,202.74787,0,0,1,362.753,403.18446a198.9214,198.9214,0,0,1-49.89819-141.13313c.28387-5.78965.76859-11.56257,1.52683-17.30975.74726-5.66395,1.7459-11.30258,2.97756-16.87354q1.89659-8.57846,4.54716-16.96232,2.572-8.15982,5.86352-16.07336c2.27456-5.48511,4.8105-10.85411,7.54648-16.122,2.88124-5.54761,6.07307-10.94466,9.4812-16.1843q4.7149-7.24866,10.04419-14.06235c3.85433-4.95083,7.95005-9.72719,12.27081-14.27641q6.54174-6.88764,13.66916-13.16194c4.83847-4.26274,9.91241-8.2498,15.13962-12.02191q4.04222-2.917,8.23639-5.61188a244.394,244.394,0,0,0-109.08168,323.486q.82056,1.704,1.66751,3.39519.38468.7681.77476,1.53348a4.60036,4.60036,0,0,0,.46987.91713c.32364.35883.40473.29867.89987.28969,2.578-.04676,5.15806-.03471,7.73585.021q7.14438.15426,14.26584.8147A199.577,199.577,0,0,1,499.05209,600.21532a201.32943,201.32943,0,0,1,.64133,60.16627A198.7484,198.7484,0,0,1,453.532,762.237a202.19273,202.19273,0,0,1-49.77669,41.78411A199.86222,199.86222,0,0,1,279.6049,830.51543a203.06671,203.06671,0,0,1-23.20252-4.02994c-4.95445-1.16337-9.85994-2.531-14.71085-4.07118a242.60378,242.60378,0,0,0,68.54706,18.97282,247.03638,247.03638,0,0,0,66.4102-.79809,243.52975,243.52975,0,0,0,108.66832-44.88732,246.04226,246.04226,0,0,0,59.73343-63.07421q1.04115-1.58711,2.05761-3.19021c.263-.41478.85074-1.06891.77441-1.50894a8.49447,8.49447,0,0,0-1.05057-1.9447q-2.00279-3.75737-3.8436-7.59815-3.698-7.71623-6.7219-15.73206a198.01918,198.01918,0,0,1-9.34078-33.367,200.87418,200.87418,0,0,1,.041-73.98045A197.88137,197.88137,0,0,1,548.324,536.05063,199.08091,199.08091,0,0,1,721.41371,432.9917q7.33682-.05093,14.66347.42449,7.066.47,14.088,1.41786,6.952.95079,13.82736,2.39389,6.73038,1.4208,13.355,3.29017,6.48924,1.84088,12.8432,4.11821,6.44067,2.31259,12.7085,5.06391,6.3891,2.79015,12.56139,6.04592,6.16559,3.24277,12.09984,6.9069c3.71356,2.2975,7.36439,4.705,10.91293,7.25121q5.48994,3.93921,10.72616,8.20808c3.603,2.93077,7.0801,6.01877,10.46193,9.202q5.40071,5.08362,10.4077,10.563,4.9978,5.44733,9.57009,11.27034c3.4,4.31833,6.6383,8.77214,9.66035,13.36317,3.10448,4.71627,6.03931,9.55546,8.74828,14.51488a200.21456,200.21456,0,0,1,10.06824,21.36634,206.9737,206.9737,0,0,1,7.97272,24.37127,243.74067,243.74067,0,0,0-51.67776-120.94126,245.663,245.663,0,0,0-75.01182-62.48165,242.85789,242.85789,0,0,0-95.94806-28.3044Q689.18423,370.69463,684.907,370.50177Z" transform="translate(-18 -20)"/></svg>
<p>Error.Title</p>
<p>Error.Description</p>
<p><span id="error-string">%%ERROR_CODE%%</span><br />
<span class="bold">URL</span>: <span id="error-url">%%ERROR_URL%%</span></p>
<p><a href="%%ERROR_URL%%" id="retry-link">Error.Retry</a>
</div>
</div>
</div>
</body>
</html>07070100000035000041ED00000000000000000000000268D5836B00000000000000000000000000000000000000000000004100000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale07070100000036000081A400000000000000000000000168D5836B000007F2000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/af-ZA.iniLocalFile="Lokale lêer"
Width="Breedte"
Height="Hoogte"
FPS="RPS"
CSS="Pasgemaakte CSS"
BrowserSource="Blaaier"
CustomFrameRate="Gebruik pasgemaakte beeldduur"
RerouteAudio="Beheer oudio d.m.v. OBS"
Inspect="Inspekteer"
DevTools="Inspekteer blaaierdok ‘%1’"
CopyUrl="Kopieer huidige adres"
WebpageControlLevel="Bladtoestemmings"
WebpageControlLevel.Level.None="Geen toegang tot OBS"
WebpageControlLevel.Level.ReadObs="Leestoegang tot OBS-statusinligting"
WebpageControlLevel.Level.ReadUser="Leestoegang tot gebruikerinligting (huidige toneelversameling, oorgange)"
WebpageControlLevel.Level.Basic="Basiese toegang tot OBS (bewaar terugspeelbuffer, ens.)"
WebpageControlLevel.Level.Advanced="Gevorderde toegang tot OBS (Verander tonele, Begin/stop herhaalbuffer, ens.)"
WebpageControlLevel.Level.All="Volle toegang tot OBS (begin/stop stroming sonder waarskuwing, ens.)"
Dialog.Alert="JavaScript-waarskuwing"
Dialog.Confirm="JavaScript-bevestiging"
Dialog.Prompt="JavaScript-melding"
Dialog.BrowserDock="Blaaierdok"
Dialog.ReceivedFrom="Ontvang van ‘%1’"
Error.Title="Kon nie daardie blad laai nie!"
Error.Description="Maak seker die adres is korrek en dat die werf nie probleme ervaar nie."
Error.Retry="Klik hier om weer te probeer"
Error.Code="Fout: %1"
Zoom.Reset="Herstel zoem"
Zoom.Out="Zoem uit"
Zoom.In="Zoem in"
ErrorCode.ERR_CONNECTION_REFUSED="Bediener het die koppeling geweier"
ErrorCode.ERR_NAME_NOT_RESOLVED="Bediener se IP-adres nie gevind nie"
ErrorCode.ERR_CONNECTION_TIMED_OUT="Verbinding het uitgetel"
ErrorCode.ERR_TIMED_OUT="Bewerking het uitgetel"
ErrorCode.ERR_FILE_NOT_FOUND="Lêer nie gevind nie"
ErrorCode.ERR_FAILED="Kon nie koppel nie"
ErrorCode.ERR_NETWORK_CHANGED="Netwerk het verander"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="SSL-weergawekoppelvlak. TLS 1.3 is dalk versper of gewysig."
ErrorCode.ERR_SSL_PROTOCOL_ERROR="SSL-protokolfout. Kon nie ’n beveiligde verbinding maak nie."
ErrorCode.ERR_CERT_DATE_INVALID="Bediener se SSL-sertifikaat is verouderd of u rekenaar se tyd is verkeerd."
07070100000037000081A400000000000000000000000168D5836B00000B8B000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/ar-SA.iniLocalFile="ملف محلي"
URL="الرابط"
Width="العرض"
Height="الارتفاع"
FPS="عدد الإطارات /ث"
CSS="CSS مخصص"
ShutdownSourceNotVisible="إيقاف تشغيل المصدر عندما لا يكون مرئياً"
RefreshBrowserActive="قم بتحديث المستعرض عندما يصبح المشهد نشطاً"
RefreshNoCache="تحديث ذاكرة التخزين المؤقت للصفحة الحالية"
BrowserSource="متصفح"
CustomFrameRate="استخدام معدل إطارات مخصص"
RerouteAudio="التحكم في الصوت عن طريق OBS"
Inspect="افحص"
DevTools="فحص مربع المتصفح '%1'"
CopyUrl="نسخ العنوان الحالي"
WebpageControlLevel="أُذُونَات الصفحة"
WebpageControlLevel.Level.None="لا يوجد إذن للوصول إلى (OBS)"
WebpageControlLevel.Level.ReadObs="قراءة الوصول إلى معلومات حالة OBS"
WebpageControlLevel.Level.ReadUser="قراءة الوصول إلى معلومات المستخدم (مجموعة المشهد الحالية، الانتقالات)"
WebpageControlLevel.Level.Basic="الوصول الأساسي ل OBS (حفظ مؤقت لإعادة العرض , إلخ..)"
WebpageControlLevel.Level.Advanced="وصول متقدم ل OBS (تغيير المشاهد, تشغيل/إيقاف إعادة العرض المؤقت, إلخ...)"
WebpageControlLevel.Level.All="الوصول الكامل ل OBS (بدء/ايقاف البث المباشر بدون إنذار, إلخ...)"
Dialog.Alert="تنبيه جافا سكريبت"
Dialog.Confirm="تأكيد جافا سكريبت"
Dialog.Prompt="مطالبة جافا سكريبت"
Dialog.BrowserDock="قائمة المتصفح"
Dialog.ReceivedFrom="استقبل من '%1'"
Error.Title="تعذر تحميل هذه الصفحة!"
Error.Description="تأكد من صحة العنوان ، وأن الموقع لا يواجه مشاكل."
Error.Retry="انقر لإعادة المحاولة"
Error.Code="خطأ: %1"
Error.URL="الرابط: %2"
Zoom.Reset="افتراضي التكبير"
Zoom.Out="تصغير"
Zoom.In="تكبير"
ErrorCode.ERR_CONNECTION_REFUSED="رفض الخادم الاتصال"
ErrorCode.ERR_NAME_NOT_RESOLVED="لم يتم العثور على عنوان IP للخادم"
ErrorCode.ERR_CONNECTION_TIMED_OUT="انتهت مهلة الاتصال"
ErrorCode.ERR_TIMED_OUT="انتهت مهلة العملية"
ErrorCode.ERR_FILE_NOT_FOUND="لم يتم العثور على الملف"
ErrorCode.ERR_FAILED="فشل في الاتصال"
ErrorCode.ERR_NETWORK_CHANGED="تم تغيير الشبكة"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="تدخّل إصدار SSL. يمكن حظر أو تعديل TLS 1.3 ."
ErrorCode.ERR_SSL_PROTOCOL_ERROR="خطأ في بروتوكول SSL. لا يمكن إجراء اتصال آمن."
ErrorCode.ERR_CERT_DATE_INVALID="شهادة SSL للخادم قديمة أو ساعة جهاز الكمبيوتر غير صحيح."
07070100000038000081A400000000000000000000000168D5836B00000938000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/az-AZ.iniLocalFile="Yerli fayl"
Width="En"
Height="Hündürlük"
CSS="Fərdi CSS"
ShutdownSourceNotVisible="Görünməyəndə mənbəni söndür"
RefreshBrowserActive="Səhnə aktivləşəndə brauzeri yenilə"
RefreshNoCache="Cari səhifənin keşini yenilə"
BrowserSource="Brauzer"
CustomFrameRate="Fərdi kadr tezliyi istifadə et"
RerouteAudio="OBS ilə səsə nəzarət"
Inspect="Yoxla"
DevTools="'%1' Brauzer Panelini Yoxla"
CopyUrl="Cari ünvanı kopyala"
WebpageControlLevel="Səhifə icazələri"
WebpageControlLevel.Level.None="OBS-ə giriş icazəsi yoxdur"
WebpageControlLevel.Level.ReadObs="OBS status məlumatlarına oxuma icazəsi"
WebpageControlLevel.Level.ReadUser="İstifadəçi məlumatlarına oxuma icazəsi (cari Səhnə Toplusu, Keçidlər)"
WebpageControlLevel.Level.Basic="OBS-ə əsas icazə (Təkrar buferini saxlamaq və s.)"
WebpageControlLevel.Level.Advanced="OBS-ə genişləndirilmiş icazə (Səhnələri dəyişmək, Təkrar buferini başlatmaq/dayandırmaq və s.)"
WebpageControlLevel.Level.All="OBS-ə tam icazə (Xəbərdarlıq etmədən yayımı Başlatmaq/Dayandırmaq və s.)"
Dialog.Alert="JavaScript Xəbərdarlığı"
Dialog.Confirm="JavaScript Təsdiqi"
Dialog.Prompt="JavaScript Sorğusu"
Dialog.BrowserDock="Brauzer Paneli"
Dialog.ReceivedFrom="'%1'-dan qəbul edildi"
Error.Title="Səhifə yüklənə bilmədi!"
Error.Description="Ünvanın düzgün olduğundan və saytda problem olmadığından əmin olun."
Error.Retry="Yenidən cəhd üçün bura klikləyin"
Error.Code="Xəta:%1"
Error.URL="URL:%2"
Zoom.Reset="Miqyası Sıfırla"
Zoom.Out="Uzaqlaşdır"
Zoom.In="Yaxınlaşdır"
ErrorCode.ERR_CONNECTION_REFUSED="Server bağlantını rədd etdi"
ErrorCode.ERR_NAME_NOT_RESOLVED="Serverin IP ünvanı tapılmadı"
ErrorCode.ERR_CONNECTION_TIMED_OUT="Bağlantı vaxtı bitdi"
ErrorCode.ERR_TIMED_OUT="Əməliyyat vaxtı bitdi"
ErrorCode.ERR_FILE_NOT_FOUND="Fayl tapılmadı"
ErrorCode.ERR_FAILED="Bağlantı uğursuz oldu"
ErrorCode.ERR_NETWORK_CHANGED="Şəbəkə dəyişdi"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="SSL versiya müdaxiləsi. TLS 1.3 bloklanmış və ya dəyişdirilmiş ola bilər."
ErrorCode.ERR_SSL_PROTOCOL_ERROR="SSL protokol xətası. Təhlükəsiz bağlantı qurula bilmədi."
ErrorCode.ERR_CERT_DATE_INVALID="Serverin SSL sertifikatı köhnəlib və ya kompüterinizin saatı düzgün deyil."
07070100000039000081A400000000000000000000000168D5836B00000070000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/ba-RU.iniLocalFile="Локаль файл"
Width="Киңлек"
Height="Бейеклек"
BrowserSource="Браузер"
0707010000003A000081A400000000000000000000000168D5836B00000C73000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/be-BY.iniLocalFile="Файл на дыску"
Width="Шырыня"
Height="Вышыня"
CSS="Уласны CSS"
ShutdownSourceNotVisible="Адключаць крыніцу, калі яе не відаць"
RefreshBrowserActive="Абнаўляць браўзер пры актывацыі сцэны"
RefreshNoCache="Абнавіць кэш бягучай старонкі"
BrowserSource="Браўзер"
CustomFrameRate="Уласная частата кадраў"
RerouteAudio="Кіраваць гукам праз OBS"
Inspect="Аглядзець"
DevTools="Аглядзець браўзерны док «%1»"
CopyUrl="Скапіяваць адрас"
WebpageControlLevel="Дазволы старонкі"
WebpageControlLevel.Level.None="Без доступу да OBS"
WebpageControlLevel.Level.ReadObs="Чытанне звестак аб стане OBS"
WebpageControlLevel.Level.ReadUser="Чытанне звестак аб карыстальніку (бягучай калекцыі сцэн, пераходах)"
WebpageControlLevel.Level.Basic="Базавы доступ да OBS (захаванне буфера паўтору і г. д.)"
WebpageControlLevel.Level.Advanced="Пашыраны доступ да OBS (змяненне сцэн, пачынанне/спыненне буфера паўтору і г. д.)"
WebpageControlLevel.Level.All="Поўны доступ да OBS (пачынанне/спыненне стрымаў без папаярэджання і г. д.)"
Dialog.Alert="Апавяшчэнне JavaScript"
Dialog.Confirm="Пацвярджэнне JavaScript"
Dialog.Prompt="Радок JavaScript"
Dialog.BrowserDock="браўзерны док"
Dialog.ReceivedFrom="Атрымана ад «%1»"
Error.Title="Не атрымалася загрузіць старонку!"
Error.Description="Пераканайцеся, што адрас правільны і што сайт працуе без праблем."
Error.Retry="Націсніце, каб паўтарыць спробу"
Error.Code="Памылка: %1"
Zoom.Reset="Скінуць маштаб"
Zoom.Out="Паменшыць"
Zoom.In="Павялічыць"
ErrorCode.ERR_CONNECTION_REFUSED="Сервер адмовіў у злучэнні"
ErrorCode.ERR_NAME_NOT_RESOLVED="IP-адрас сервера не знойдзены"
ErrorCode.ERR_CONNECTION_TIMED_OUT="Час чакання злучэння скончыўся"
ErrorCode.ERR_TIMED_OUT="Час чакання аперацыі скончыўся"
ErrorCode.ERR_FILE_NOT_FOUND="Файл не знойдзены"
ErrorCode.ERR_FAILED="Не атрымалася злучыцца"
ErrorCode.ERR_NETWORK_CHANGED="Сетка змянілася"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="Канфлікт версій SSL. Магчымае блакіраванне або мадыфікацыя TLS 1.3."
ErrorCode.ERR_SSL_PROTOCOL_ERROR="Памылка пратакола SSL. Не атрымалася наладзіць бяспечнае злучэнне."
ErrorCode.ERR_CERT_DATE_INVALID="Серверны сертыфікат SSL састарэў, або на вашым камп'ютары няправільна зададзены час."
0707010000003B000081A400000000000000000000000168D5836B00000B2C000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/bg-BG.iniLocalFile="Локален файл"
URL="URL адрес"
Width="Ширина"
Height="Височина"
FPS="Кдр. в сек."
CSS="Потребителски CSS"
ShutdownSourceNotVisible="Изключване на източника, когато не е видим"
RefreshBrowserActive="Опресняване на браузъра, когато сцената стане активна"
RefreshNoCache="Опресняване на кеша на текущата страница"
BrowserSource="Браузър"
CustomFrameRate="Използване на настроена кадрова честота"
RerouteAudio="Управление на звука чрез OBS"
Inspect="Погледни"
CopyUrl="Копирай текущия адрес"
WebpageControlLevel="Позволения на страницата"
WebpageControlLevel.Level.None="Няма достъп до OBS"
WebpageControlLevel.Level.ReadObs="Прочети достъп до OBS статус информация"
WebpageControlLevel.Level.ReadUser="Прочети достъп до информация за потребителя (текуща Сценна Колекция, Транзиция)"
WebpageControlLevel.Level.All="Пълен достъп към OBS (Стартиране/Спиране на стрийма без предупреждение и др.)"
Dialog.ReceivedFrom="Получено от '%1'"
Error.Title="Страницата не би могла да се зареди!"
Error.Description="Уверете се в правилността на адреса и че сайтът няма проблеми."
Error.Retry="Щракнете тук за нов опит"
Error.Code="Грешка: %1"
Error.URL="URL адрес: %2"
Zoom.Reset="Нулиране на мащабирането"
Zoom.Out="Отдалечи"
Zoom.In="Увеличи"
ErrorCode.ERR_CONNECTION_REFUSED="Сървърът отказа свързването"
ErrorCode.ERR_NAME_NOT_RESOLVED="IP адреса на сървъра не можа да бъде намерен"
ErrorCode.ERR_CONNECTION_TIMED_OUT="Времето за изчакване изтече"
ErrorCode.ERR_TIMED_OUT="Операцията изтече"
ErrorCode.ERR_FILE_NOT_FOUND="Файлът не е намерен"
ErrorCode.ERR_FAILED="Неуспех при свързването"
ErrorCode.ERR_NETWORK_CHANGED="Променена мрежа"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="SSL интерфейс версия. TLS 1.3 може би е блокиран или променен"
ErrorCode.ERR_SSL_PROTOCOL_ERROR="Грешка на протокол SSL. Не би могло да се осъществи сигурно свързване."
ErrorCode.ERR_CERT_DATE_INVALID="SSL сертификата на сървъра е стар или времето на вашия компютър е грешен."
0707010000003C000081A400000000000000000000000168D5836B00000BBA000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/bn-BD.iniLocalFile="স্থানীয় ফাইল"
URL="ইউআরএল"
Width="প্রস্থ"
Height="উচ্চতা"
FPS="এফপিএস"
CSS="কাস্টম সিএসএস"
ShutdownSourceNotVisible="দৃশ্যমান না হলে সোর্স বন্ধ করুন"
RefreshBrowserActive="দৃশ্য সক্রিয় হয়ে গেলে ব্রাউজারটি রিফ্রেশ করুন"
RefreshNoCache="বর্তমান পৃষ্ঠার ক্যাশে রিফ্রেশ করুন"
BrowserSource="ব্রাউজার"
CustomFrameRate="পছন্দসই ফ্রেম হার ব্যবহার করুন"
RerouteAudio="OBS এর মাধ্যমে অডিও নিয়ন্ত্রণ করুন"
WebpageControlLevel="পেজ অনুমতি"
WebpageControlLevel.Level.None="OBS এ কোনো অ্যাক্সেস নেই"
WebpageControlLevel.Level.Basic="OBS তে প্রাথমিক অ্যাক্সেস (রিপ্লে বাফার সংরক্ষণ, ইত্যাদি)"
WebpageControlLevel.Level.Advanced="OBS এ উন্নত অ্যাক্সেস (দৃশ্য পরিবর্তন, রিপ্লে বাফার শুরু/বন্ধ, ইত্যাদি)"
WebpageControlLevel.Level.All="OBS এ সম্পূর্ণ অ্যাক্সেস (সতর্কতা ছাড়াই স্ট্রিমিং শুরু/বন্ধ, ইত্যাদি)"
Error.Title="পেজটি লোড করা যায়নি!"
Error.Description="ঠিকানাটি সঠিক কিনা এবং সাইটটিতে কোনও সমস্যা হচ্ছে না তা নিশ্চিত করুন।"
Error.Retry="আবার চেষ্টা করতে এখানে ক্লিক করুন"
Error.Code="ত্রুটি: %1"
Error.URL="ইউআরএল: %2"
ErrorCode.ERR_CONNECTION_REFUSED="সার্ভার সংযোগ প্রত্যাখ্যান করেছে"
ErrorCode.ERR_NAME_NOT_RESOLVED="সার্ভারের আইপি অ্যাড্রেস পাওয়া যায়নি"
ErrorCode.ERR_CONNECTION_TIMED_OUT="সংযোগের সময় শেষ"
ErrorCode.ERR_FILE_NOT_FOUND="ফাইল খুঁজে পাওয়া যায়নি"
ErrorCode.ERR_FAILED="সংযোগ ব্যর্থ হয়েছে"
ErrorCode.ERR_NETWORK_CHANGED="নেটওয়ার্ক পরিবর্তিত হয়েছে"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="SSL সংস্করণ হস্তক্ষেপ। TLS 1.3 রোধ বা সংশোধন করা হতে পারে।"
ErrorCode.ERR_SSL_PROTOCOL_ERROR="SSL প্রোটোকল ত্রুটি। একটি নিরাপদ সংযোগ করা যায়নি."
0707010000003D000081A400000000000000000000000168D5836B000009A2000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/ca-ES.iniLocalFile="Fitxer local"
Width="Amplada"
Height="Alçada"
CSS="CSS personalizat"
ShutdownSourceNotVisible="Apaga la font quan no sigui visible"
RefreshBrowserActive="Actualitza el navegador en activar-se l'escena"
RefreshNoCache="Actualitza la memòria cau de la pàgina actual"
BrowserSource="Navegador"
CustomFrameRate="Utilitza una taxa de fotogrames personalitzada"
RerouteAudio="Control d'àudio mitjançant l'OBS"
Inspect="Inspecciona"
DevTools="Inspecciona el panell del navegador '%1'"
CopyUrl="Copia l'adreça actual"
WebpageControlLevel="Permisos de la pàgina"
WebpageControlLevel.Level.None="Sense accés a l'OBS"
WebpageControlLevel.Level.ReadObs="Accés de lectura a la informació d'estat de l'OBS"
WebpageControlLevel.Level.ReadUser="Accés de lectura a la informació de l'usuari (col·lecció d'escenes actual, transicions)"
WebpageControlLevel.Level.Basic="Accés bàsic a l'OBS (desa la memòria intermèdia de reproducció, etc.)"
WebpageControlLevel.Level.Advanced="Accés avançat a l'OBS (canvia d'escenes, inicia/atura la memòria intermèdia de reproducció, etc.)"
WebpageControlLevel.Level.All="Accés complet a l'OBS (inicia/atura la transmissió sense avís, etc.)"
Dialog.Alert="Avís del JavaScript"
Dialog.Confirm="Confirmació del JavaScript"
Dialog.Prompt="Sol·licitud del JavaScript"
Dialog.BrowserDock="Acoblador del navegador"
Dialog.ReceivedFrom="Rebut de «%1»"
Error.Title="No s'ha pogut carregar la pàgina!"
Error.Description="Assegureu-vos que l'adreça sigui correcta i que el lloc no tingui problemes."
Error.Retry="Feu clic aquí per tornar a provar"
Zoom.Reset="Reinicia el zoom"
Zoom.Out="Redueix"
Zoom.In="Amplia"
ErrorCode.ERR_CONNECTION_REFUSED="El servidor ha rebutjat la connexió"
ErrorCode.ERR_NAME_NOT_RESOLVED="No s'ha trobat l'adreça IP del servidor"
ErrorCode.ERR_CONNECTION_TIMED_OUT="S'ha esgotat el temps de la connexió"
ErrorCode.ERR_TIMED_OUT="S'ha esgotat el temps d'espera de l'operació"
ErrorCode.ERR_FILE_NOT_FOUND="No s'ha trobat el fitxer"
ErrorCode.ERR_FAILED="Error en connectar"
ErrorCode.ERR_NETWORK_CHANGED="La xarxa ha canviat"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="Interferència de la versió SSL. És possible que TLS 1.3 estigui blocat o modificat."
ErrorCode.ERR_SSL_PROTOCOL_ERROR="Error de protocol SSL. No s'ha pogut establir una connexió segura."
ErrorCode.ERR_CERT_DATE_INVALID="El certificat SSL del servidor està obsolet o l'hora del vostre ordinador és incorrecta."
0707010000003E000081A400000000000000000000000168D5836B00000930000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/cs-CZ.iniLocalFile="Místní soubor"
URL="Adresa URL"
Width="Šířka"
Height="Výška"
CSS="Vlastní styly"
ShutdownSourceNotVisible="Vypnout zdroj, pokud není vidět"
RefreshBrowserActive="Obnovit stránku při aktivaci scény"
RefreshNoCache="Vyčistit mezipaměť (cache) aktuální stránky"
BrowserSource="Prohlížeč"
CustomFrameRate="Použít vlastní snímkovací frekvenci"
RerouteAudio="Ovládat zvuk pomocí OBS"
Inspect="Prozkoumat"
DevTools="Prozkoumat dok prohlížeče '%1'"
CopyUrl="Zkopírovat aktuální adresu"
WebpageControlLevel="Oprávnění stránky"
WebpageControlLevel.Level.None="Žádný přístup k OBS"
WebpageControlLevel.Level.ReadObs="Může číst informace o stavu OBS"
WebpageControlLevel.Level.ReadUser="Může číst uživatelské informace (aktuální kolekce scén, přechody)"
WebpageControlLevel.Level.Basic="Základní přístup k OBS (Uložit záznam, etc.)"
WebpageControlLevel.Level.Advanced="Pokročilý přístup k OBS (Změnit scény, spustit/zastavit záznam do paměti, etc.)"
WebpageControlLevel.Level.All="Plný přístup k OBS (Začít/Ukončit vysílání bez varování, etc.)"
Dialog.Alert="Upozornění JavaScriptu"
Dialog.Confirm="Otázka JavaScriptu"
Dialog.Prompt="Výzva JavaScriptu"
Dialog.BrowserDock="Dok prohlížeče"
Dialog.ReceivedFrom="Přijato od '%1'"
Error.Title="Stránku se nepodařilo načíst!"
Error.Description="Zkontroluj, zda je adresa správná a cílové stránky nemají potíže."
Error.Retry="Kliknutím zkuste znovu"
Error.Code="Chyba: %1"
Error.URL="Adresa: %2"
Zoom.Reset="Resetovat přiblížení"
Zoom.Out="Oddálit"
Zoom.In="Přiblížit"
ErrorCode.ERR_CONNECTION_REFUSED="Server odmítl připojení"
ErrorCode.ERR_NAME_NOT_RESOLVED="IP adresa serveru nebyla nalezena"
ErrorCode.ERR_CONNECTION_TIMED_OUT="Vypršel čas připojování"
ErrorCode.ERR_TIMED_OUT="Čas operace vypršel"
ErrorCode.ERR_FILE_NOT_FOUND="Soubor nenalezen"
ErrorCode.ERR_FAILED="Spojení se nezdařilo"
ErrorCode.ERR_NETWORK_CHANGED="Síť změněna"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="Ovlivnění SSL verze. TLS 1.3 může být blokována nebo modifikována."
ErrorCode.ERR_SSL_PROTOCOL_ERROR="Chyba protokolu SSL. Nepodařilo se vytvořit bezpečné připojení."
ErrorCode.ERR_CERT_DATE_INVALID="SSL certifikát serveru je zastaralý nebo nemáte nastaven správný čas počítače."
0707010000003F000081A400000000000000000000000168D5836B0000084C000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/da-DK.iniLocalFile="Lokal fil"
Width="Bredde"
Height="Højde"
CSS="Tilpasset CSS"
ShutdownSourceNotVisible="Luk kilden, når den ikke er synlig"
RefreshBrowserActive="Opfrisk browseren, når scenen bliver aktiv"
RefreshNoCache="Opfrisk aktuelle side-cache"
CustomFrameRate="Benyt tilpasset billedhastighed"
RerouteAudio="Styr lyd via OBS"
Inspect="Inspicer"
DevTools="Inspicer Browser Dock '%1'"
CopyUrl="Kopier nuværende adresse"
WebpageControlLevel="Sidetilladelser"
WebpageControlLevel.Level.None="Ingen adgang til OBS"
WebpageControlLevel.Level.ReadObs="Læseadgang til OBSs statusinformation"
WebpageControlLevel.Level.ReadUser="Læseadgang til brugerinformation (aktuel Scenesamling, Overgange)"
WebpageControlLevel.Level.Basic="Basisadgang til OBS (Gem genafspilningsbuffer mv.)"
WebpageControlLevel.Level.Advanced="Avanceret adgang til OBS (Skift scener, Start/Stop genafsp.buffer mv.)"
WebpageControlLevel.Level.All="Fuld adgang til OBS (Start/Stop streaming uden advarsel mv.)"
Dialog.Alert="JavaScript-advarsel"
Dialog.Confirm="JavaScript-bekræftelse"
Dialog.Prompt="JavaScript-prompt"
Dialog.BrowserDock="Browser-dok"
Dialog.ReceivedFrom="Modtaget fra '%1'"
Error.Title="Siden kunne ikke indlæses!"
Error.Description="Sørg for, at adressen er korrekt, og at webstedet ikke har problemer."
Error.Retry="Klik hér, for at prøve igen"
Error.Code="Fejl: %1"
Zoom.Reset="Nulstil zoom"
Zoom.Out="Zoom ud"
Zoom.In="Zoom ind"
ErrorCode.ERR_CONNECTION_REFUSED="Server afviste forbindelsen"
ErrorCode.ERR_NAME_NOT_RESOLVED="Server IP-adressen ikke fundet"
ErrorCode.ERR_CONNECTION_TIMED_OUT="Forbindelsen fik timeout."
ErrorCode.ERR_TIMED_OUT="Handlingen fik timeout"
ErrorCode.ERR_FILE_NOT_FOUND="Fil ikke fundet"
ErrorCode.ERR_FAILED="Mislykkedes at forbinde"
ErrorCode.ERR_NETWORK_CHANGED="Netværk ændret"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="SSL-versionsinterferens. TLS 1.3 blokeres eller ændres muligvis."
ErrorCode.ERR_SSL_PROTOCOL_ERROR="SSL-protokolfejl. Sikker forbindelse kunne ikke oprettes."
ErrorCode.ERR_CERT_DATE_INVALID="Serverens SSL-certifikat er forældet, eller computerens klokkeslæt er forkert."
07070100000040000081A400000000000000000000000168D5836B00000952000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/de-DE.iniLocalFile="Lokale Datei"
Width="Breite"
Height="Höhe"
CSS="Benutzerdefiniertes CSS"
ShutdownSourceNotVisible="Deaktivieren, wenn Quelle nicht sichtbar ist"
RefreshBrowserActive="Browser bei Szenenaktivierung aktualisieren"
RefreshNoCache="Cache der aktuellen Seite aktualisieren"
CustomFrameRate="Benutzerdefinierte Framerate verwenden"
RerouteAudio="Audio über OBS steuern"
Inspect="Untersuchen"
DevTools="Browserdock „%1“ untersuchen"
CopyUrl="Adresse kopieren"
WebpageControlLevel="Seitenberechtigungen"
WebpageControlLevel.Level.None="Kein Zugriff auf OBS"
WebpageControlLevel.Level.ReadObs="Lesezugriff auf OBS-Statusinformation"
WebpageControlLevel.Level.ReadUser="Lesezugriff auf Benutzerinformationen (aktuelle Szenensammlung & Übergänge)"
WebpageControlLevel.Level.Basic="Grundlegender Zugriff auf OBS (Wiederholungspuffer speichern, etc.)"
WebpageControlLevel.Level.Advanced="Erweiterter Zugriff auf OBS (Szenen wechseln, Wiederholungspuffer starten/beenden, etc.)"
WebpageControlLevel.Level.All="Voller Zugriff auf OBS (Stream ohne Warnung starten/beenden, etc.)"
Dialog.Alert="JavaScript-Alarm"
Dialog.Confirm="JavaScript-Bestätigung"
Dialog.Prompt="JavaScript-Aufforderung"
Dialog.BrowserDock="Browser-Dock"
Dialog.ReceivedFrom="Erhalten von „%1“"
Error.Title="Die Seite konnte nicht geladen werden."
Error.Description="Stellen Sie sicher, dass die Adresse korrekt ist und dass die Seite keine Probleme hat."
Error.Retry="Hier klicken, um es erneut zu versuchen"
Error.Code="Fehler: %1"
Zoom.Reset="Zoom zurücksetzen"
Zoom.Out="Herauszoomen"
Zoom.In="Hineinzoomen"
ErrorCode.ERR_CONNECTION_REFUSED="Server hat die Verbindung abgelehnt"
ErrorCode.ERR_NAME_NOT_RESOLVED="IP-Adresse des Servers nicht gefunden"
ErrorCode.ERR_CONNECTION_TIMED_OUT="Zeitüberschreitung bei Verbindung"
ErrorCode.ERR_TIMED_OUT="Zeitüberschreitung der Operation"
ErrorCode.ERR_FILE_NOT_FOUND="Datei nicht gefunden"
ErrorCode.ERR_FAILED="Verbindung fehlgeschlagen"
ErrorCode.ERR_NETWORK_CHANGED="Netzwerk geändert"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="SSL-Versionsinterferenz. TLS 1.3 ist möglicherweise blockiert oder verändert."
ErrorCode.ERR_SSL_PROTOCOL_ERROR="SSL-Protokollfehler. Es konnte keine sichere Verbindung hergestellt werden."
ErrorCode.ERR_CERT_DATE_INVALID="Entweder ist das SSL-Zertifikat des Servers veraltet oder die Uhrzeit Ihres Computers nicht korrekt."
07070100000041000081A400000000000000000000000168D5836B00000E00000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/el-GR.iniLocalFile="Τοπικό αρχείο"
URL="Διεύθυνση URL"
Width="Πλάτος"
Height="Ύψος"
FPS="Καρέ ανά δευτερόλεπτο"
CSS="Προσαρμοσμένο CSS"
ShutdownSourceNotVisible="Τερματισμος λειτουργίας πηγής, όταν δεν είναι ορατή"
RefreshBrowserActive="Ανανέωση του προγράμματος περιήγησης, όταν η σκηνή γίνεται ενεργή"
RefreshNoCache="Ανανέωση της μνήμης cache της τρέχουσας σελίδας"
BrowserSource="Πρόγραμμα περιήγησης"
CustomFrameRate="Χρήση προσαρμοσμένου ρυθμού καρέ"
RerouteAudio="Έλεγχος ήχου μέσω OBS"
CopyUrl="Αντιγραφή τρέχουσας διεύθυνσης"
WebpageControlLevel="Δικαιώματα σελίδας"
WebpageControlLevel.Level.None="Καμία πρόσβαση στο OBS"
WebpageControlLevel.Level.ReadObs="Πρόσβαση στις πληροφορίες κατάστασης του OBS"
WebpageControlLevel.Level.ReadUser="Πρόσβαση στις πληροφορίες χρήστη (τρέχουσα συλλογή σκηνών, μεταβάσεις)"
WebpageControlLevel.Level.Basic="Βασική πρόσβαση στο OBS (Αποθήκευση buffer επανάληψης, κλπ.)"
WebpageControlLevel.Level.Advanced="Προηγμένη πρόσβαση στο OBS (Αλλαγή σκηνών, αποθήκη εκκίνησης/διακοπής buffer επανάληψης, κλπ.)"
WebpageControlLevel.Level.All="Πλήρης πρόσβαση στο OBS (Εκκίνηση/Διακοπή ροής χωρίς προειδοποίηση, κλπ.)"
Dialog.Alert="Ειδοποίηση JavaScript"
Dialog.Confirm="Επιβεβαίωση JavaScript"
Dialog.Prompt="Υπενθύμιση JavaScript"
Dialog.BrowserDock="Προσάρτηση Περιηγητή"
Dialog.ReceivedFrom="Λήφθηκε από '%1'"
Error.Title="Δεν ήταν δυνατή η φόρτωση της εικόνας"
Error.Description="Βεβαιωθείτε ότι η διεύθυνση είναι σωστή και ότι ο ιστότοπος δεν έχει προβλήματα."
Error.Retry="Κάντε κλικ εδώ, για να προσπαθήσετε ξανά"
Error.Code="Σφάλμα: %1"
Error.URL="Σύνδεσμος: %2"
Zoom.Reset="Επαναφορά Κλίμακας"
Zoom.Out="Σμίκρυνση"
Zoom.In="Μεγέθυνση"
ErrorCode.ERR_CONNECTION_REFUSED="Ο διακομιστής αρνήθηκε τη σύνδεση"
ErrorCode.ERR_NAME_NOT_RESOLVED="Η διεύθυνση IP του διακομιστή δεν βρέθηκε"
ErrorCode.ERR_CONNECTION_TIMED_OUT="Λήξη χρονικού ορίου σύνδεσης"
ErrorCode.ERR_TIMED_OUT="Λήξη χρονικού ορίου λειτουργίας"
ErrorCode.ERR_FILE_NOT_FOUND="Το αρχείο δε βρέθηκε"
ErrorCode.ERR_FAILED="Η σύνδεση απέτυχε"
ErrorCode.ERR_NETWORK_CHANGED="Το δίκτυο άλλαξε"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="Πρόβλημα έκδοσης SSL. Το TLS 1.3 μπορεί να αποκλειστεί ή να τροποποιηθεί."
ErrorCode.ERR_SSL_PROTOCOL_ERROR="Σφάλμα πρωτοκόλλου SSL. Δεν ήταν δυνατή η ασφαλής σύνδεση."
ErrorCode.ERR_CERT_DATE_INVALID="Το πιστοποιητικό SSL του διακομιστή έχει λήξη ή το ρολόι του υπολογιστή σας είναι αποχρονισμένο."
07070100000042000081A400000000000000000000000168D5836B00000017000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/en-GB.iniLocalFile="Local File"
07070100000043000081A400000000000000000000000168D5836B000009C2000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/en-US.iniLocalFile="Local file"
URL="URL"
Width="Width"
Height="Height"
FPS="FPS"
CSS="Custom CSS"
ShutdownSourceNotVisible="Shutdown source when not visible"
RefreshBrowserActive="Refresh browser when scene becomes active"
RefreshNoCache="Refresh cache of current page"
BrowserSource="Browser"
CustomFrameRate="Use custom frame rate"
RerouteAudio="Control audio via OBS"
Inspect="Inspect"
DevTools="Inspect Browser Dock '%1'"
CopyUrl="Copy current address"
WebpageControlLevel="Page permissions"
WebpageControlLevel.Level.None="No access to OBS"
WebpageControlLevel.Level.ReadObs="Read access to OBS status information"
WebpageControlLevel.Level.ReadUser="Read access to user information (current Scene Collection, Transitions)"
WebpageControlLevel.Level.Basic="Basic access to OBS (Save replay buffer, etc.)"
WebpageControlLevel.Level.Advanced="Advanced access to OBS (Change scenes, Start/Stop replay buffer, etc.)"
WebpageControlLevel.Level.All="Full access to OBS (Start/Stop streaming without warning, etc.)"
Dialog.Alert="JavaScript Alert"
Dialog.Confirm="JavaScript Confirm"
Dialog.Prompt="JavaScript Prompt"
Dialog.BrowserDock="Browser Dock"
Dialog.ReceivedFrom="Received from '%1'"
Error.Title="Couldn't load that page!"
Error.Description="Make sure the address is correct, and that the site isn't having issues."
Error.Retry="Click here to retry"
Error.Code="Error: %1"
Error.URL="URL: %2"
Zoom.Reset="Reset Zoom"
Zoom.Out="Zoom out"
Zoom.In="Zoom in"
# Error codes in CEF are fetched direct from Chromium
# CEF Reference: https://bitbucket.org/chromiumembedded/cef/src/master/include/base/internal/cef_net_error_list.h
# Chromium Reference: https://chromium.googlesource.com/chromium/src/+/master/net/base/net_error_list.h#
# Cleaned up list of codes: https://gist.github.com/WizardCM/1acead41cce983d54996a1ee484a4523
ErrorCode.ERR_CONNECTION_REFUSED="Server refused the connection"
ErrorCode.ERR_NAME_NOT_RESOLVED="Server's IP address not found"
ErrorCode.ERR_CONNECTION_TIMED_OUT="Connection timed out"
ErrorCode.ERR_TIMED_OUT="Operation timed out"
ErrorCode.ERR_FILE_NOT_FOUND="File not found"
ErrorCode.ERR_FAILED="Failed to connect"
ErrorCode.ERR_NETWORK_CHANGED="Network changed"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="SSL version interference. TLS 1.3 might be blocked or modified."
ErrorCode.ERR_SSL_PROTOCOL_ERROR="SSL protocol error. Could not make a secure connection."
ErrorCode.ERR_CERT_DATE_INVALID="Server's SSL certificate is outdated or your computer's time is incorrect."
07070100000044000081A400000000000000000000000168D5836B0000025A000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/eo-UY.iniLocalFile="Loka dosiero"
Height="Alteco"
BrowserSource="Retumilo"
Inspect="Inspekti"
Error.Title="Ne eblis ŝargi tiun paĝon!"
Error.Description="Certu ke la adreso ĝustas, kaj ke la reto ne havas problemojn"
Error.Retry="Klaku ĉi tie por reprovi"
Error.Code="Eraro: %1"
ErrorCode.ERR_CONNECTION_REFUSED="Servilo rifuzis la konekton"
ErrorCode.ERR_NAME_NOT_RESOLVED="IP adreso de servilo ne trovita"
ErrorCode.ERR_CONNECTION_TIMED_OUT="Tempolimo de konekto"
ErrorCode.ERR_FILE_NOT_FOUND="Dosiero ne trovita"
ErrorCode.ERR_FAILED="Malsukcesis konekto"
ErrorCode.ERR_NETWORK_CHANGED="Reto sanĝiĝis"
07070100000045000081A400000000000000000000000168D5836B0000094D000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/es-ES.iniLocalFile="Archivo local"
Width="Ancho"
Height="Alto"
CSS="CSS personalizado"
ShutdownSourceNotVisible="Apagar fuente cuando no sea visible"
RefreshBrowserActive="Actualizar el navegador cuando la escena se active"
RefreshNoCache="Actualizar la caché de la página actual"
BrowserSource="Navegador"
CustomFrameRate="Usar frecuencia de imágenes personalizada"
RerouteAudio="Controlar audio vía OBS"
Inspect="Inspeccionar"
DevTools="Inspeccionar el panel de navegador '%1'"
CopyUrl="Copia la dirección actual"
WebpageControlLevel="Permisos de página"
WebpageControlLevel.Level.None="Sin acceso a OBS"
WebpageControlLevel.Level.ReadObs="Acceso de lectura a la información de estado de OBS"
WebpageControlLevel.Level.ReadUser="Acceso de lectura a la información de usuario (colección de escenas actual, transiciones)"
WebpageControlLevel.Level.Basic="Acceso básico a OBS (Guardar búfer de repetición, etc.)"
WebpageControlLevel.Level.Advanced="Acceso avanzado a OBS (Cambiar escenas, Iniciar/Detener búfer de repetición, etc.)"
WebpageControlLevel.Level.All="Acceso completo a OBS (Inicio/Detener transmisión sin previo aviso, etc.)"
Dialog.Alert="Alerta JavaScript"
Dialog.Confirm="Confirmar JavaScript"
Dialog.Prompt="Solicitud JavaScript"
Dialog.BrowserDock="Panel de navegador"
Dialog.ReceivedFrom="Recibido de '%1'"
Error.Title="¡No se pudo cargar esa página!"
Error.Description="Asegúrese de que la dirección es correcta, y que la web no está teniendo problemas."
Error.Retry="Haz clic aquí para reintentarlo"
Zoom.Reset="Reiniciar el zoom"
Zoom.Out="Alejar el zoom"
Zoom.In="Acercar el zoom"
ErrorCode.ERR_CONNECTION_REFUSED="El servidor rechazó la conexión"
ErrorCode.ERR_NAME_NOT_RESOLVED="Dirección IP del servidor no encontrada"
ErrorCode.ERR_CONNECTION_TIMED_OUT="Tiempo de conexión agotado"
ErrorCode.ERR_TIMED_OUT="Tiempo de espera de la operación agotado"
ErrorCode.ERR_FILE_NOT_FOUND="Archivo no encontrado"
ErrorCode.ERR_FAILED="Error al conectarse"
ErrorCode.ERR_NETWORK_CHANGED="Se ha cambiado de red"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="Interferencia de versión SSL. TLS 1.3 tal vez sea bloqueado o modificado."
ErrorCode.ERR_SSL_PROTOCOL_ERROR="Error de protocolo SSL. No se pudo hacer una conexión segura."
ErrorCode.ERR_CERT_DATE_INVALID="El certificado SSL del servidor está obsoleto o la fecha de su computadora es incorrecta."
07070100000046000081A400000000000000000000000168D5836B00000583000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/et-EE.iniLocalFile="Kohalik fail"
Width="Laius"
Height="Kõrgus"
CSS="Kohandatud CSS"
RefreshBrowserActive="Värskenda brauserit, kui stseen muutub aktiivseks"
BrowserSource="Brauser"
CustomFrameRate="Kasuta kohandatud kaadrisagedust"
Inspect="Kontrolli"
DevTools="Kontrolli brauseri dokki \"%1\""
WebpageControlLevel.Level.Basic="Baasjuurdepääs OBS-le (salvesta taasesituse puhver jne)"
WebpageControlLevel.Level.Advanced="Täiustatud juurdepääs OBS-ile (stseenide muutmine, taasesituse puhvri käivitamine/peatamine jne)"
WebpageControlLevel.Level.All="Täielik juurdepääs OBS-ile (alusta/lõpeta voogedastust ilma hoiatuseta jne)"
Dialog.Alert="JavaScript hoiatus"
Dialog.Confirm="JavaScript kinnitus"
Dialog.Prompt="JavaScript küsimus"
Dialog.BrowserDock="Brauseri dokk"
Error.Code="Viga: %1"
Zoom.Out="Vähenda"
Zoom.In="Suurenda"
ErrorCode.ERR_NAME_NOT_RESOLVED="Server IP aadressi ei leitud"
ErrorCode.ERR_CONNECTION_TIMED_OUT="Ühendus on aegunud"
ErrorCode.ERR_TIMED_OUT="Toiming on aegunud"
ErrorCode.ERR_FILE_NOT_FOUND="Faili ei leitud"
ErrorCode.ERR_FAILED="Ühendamine ei õnnestunud"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="SSL-i versiooni häired. TLS 1.3 võib olla blokeeritud või muudetud."
ErrorCode.ERR_SSL_PROTOCOL_ERROR="SSL protokolli viga. Ei õnnestunud luua turvalist ühendust."
ErrorCode.ERR_CERT_DATE_INVALID="Serveri SSL-sertifikaat on vananenud või sinu arvuti kellaaeg on vale."
07070100000047000081A400000000000000000000000168D5836B000008F1000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/eu-ES.iniLocalFile="Bertako fitxategia"
Width="Zabalera"
Height="Altuera"
CSS="CSS pertsonalizatua"
ShutdownSourceNotVisible="Itzali iturburua ikusgai ez dagoenean"
RefreshBrowserActive="Freskatu nabigatzailea eszena aktibatzen denean"
RefreshNoCache="Freskatu uneko orriaren cachea"
BrowserSource="Nabigatzailea"
CustomFrameRate="Erabili fotograma-emari pertsonalizatua"
RerouteAudio="Kontrolatu audioa OBS bidez"
Inspect="Ikuskatu"
DevTools="Ikuskatu nabigatzailearen '%1' panela"
CopyUrl="Kopiatu uneko helbidea"
WebpageControlLevel="Orrialde baimenak"
WebpageControlLevel.Level.None="Ez dago sarbiderik OBSra"
WebpageControlLevel.Level.ReadObs="Irakurri OBSrako sarbidearen informazio maila"
WebpageControlLevel.Level.ReadUser="Irakurri sarbidea erabiltzailearen informaziora"
WebpageControlLevel.Level.Basic="Oinarrizko sarbidea OBSra"
WebpageControlLevel.Level.Advanced="Sarbide Aurreratua OBSra ( Eszenak aldatu, Hasi/Gelditu errepikatu buffereratzea, etab.)"
WebpageControlLevel.Level.All="Sarbide osoa OBSra (Hasi/Geldi streaming saioa abisu barik, etab.)"
Dialog.Alert="JavaScript Alerta"
Dialog.Confirm="JavaScript baieztapena"
Dialog.Prompt="JavaScript mezua"
Dialog.BrowserDock="Nabigatzaile portua"
Dialog.ReceivedFrom="Hemendik jasota '%1'"
Error.Title="Ezin izan da orri hori kargatu!"
Error.Description="Ziurtatu helbidea zuzena dela, eta ez lekua ez daukala arazorik."
Error.Retry="Egin klik hemen berriro saiatzeko"
Error.Code="Errorea: %1"
Zoom.Reset="Berrezarri zooma"
Zoom.Out="Txikiagotu"
Zoom.In="Handiagotu"
ErrorCode.ERR_CONNECTION_REFUSED="Zerbitzariak konexioa errefuxatu du"
ErrorCode.ERR_NAME_NOT_RESOLVED="Zerbitzariaren IP helbidea ez da aurkitu"
ErrorCode.ERR_CONNECTION_TIMED_OUT="Konexioaren denbora bukatu da"
ErrorCode.ERR_TIMED_OUT="Operazioaren denbora bukatu da"
ErrorCode.ERR_FILE_NOT_FOUND="Fitxategia ez da aurkitu"
ErrorCode.ERR_FAILED="Ezin izan da konektatu"
ErrorCode.ERR_NETWORK_CHANGED="Sarea aldatuta"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="SSL bertzioaren interferentzia. TLS 1.3a blokeatuta edo aldatuta egon daiteke."
ErrorCode.ERR_SSL_PROTOCOL_ERROR="SSL protokolo errorea. Ezin izan da gai konexio segurua egiteko."
ErrorCode.ERR_CERT_DATE_INVALID="SSL serbidorearen zertifikadoa dago zaharkituta edo zure ordenagailuaren ordua txarto dago."
07070100000048000081A400000000000000000000000168D5836B00000BA6000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/fa-IR.iniLocalFile="پروندهٔ محلی"
URL="آدرس اینترنتی"
Width="عرض"
Height="ارتفاع"
FPS="فریم در ثانیه (اف پی اس)"
CSS="css سفارشی"
ShutdownSourceNotVisible="خاموش کردن منبع وقتی قابل رویت نیست"
RefreshBrowserActive="تازه کردن مرورگر که صحنه فعال می شود"
RefreshNoCache="تازه کردن حافظه نهان صفحه کنونی"
BrowserSource="مرورگر"
CustomFrameRate="نرخ فریم های سفارشی استفاده"
RerouteAudio="کنترل صدا از طریق OBS"
Inspect="وارسی"
DevTools="بازرسی داک مرورگر«%1»"
CopyUrl="آدرس فعلی را کپی کنید"
WebpageControlLevel="دسترسی های صفحه"
WebpageControlLevel.Level.None="دسترسی به OBS ندارد"
WebpageControlLevel.Level.ReadObs="خواندن دسترسی به اطلاعات وضعیت OBS"
WebpageControlLevel.Level.ReadUser="دسترسی خواندن به اطلاعات کاربر (مجموعه صحنه فعلی، انتقال)"
WebpageControlLevel.Level.Basic="دسترسی های پایه به OBS (ذخیره ریپلی بافر و...)"
WebpageControlLevel.Level.Advanced="دسترسیهای پیشرفته به OBS (تغییر صحنهها، قطع و وصل بافر بازپخش و...)"
WebpageControlLevel.Level.All="دسترسی کامل به OBS (قطع و وصل پخش بدون هشدار و...)"
Dialog.Alert="هشدار جاوا اسکریپت"
Dialog.Confirm="تایید جاوا اسکریپت"
Dialog.Prompt="درخواست جاوا اسکریپت"
Dialog.BrowserDock="داک مرورگر"
Dialog.ReceivedFrom="دریافت شده از\"%1\""
Error.Title="صفحه بارگیری نشد!"
Error.Description="اطمینان حاصل کنید که آدرس صحیح است و سایت با مشکلی روبرو نیست."
Error.Retry="کلیک برای امتحان دوباره"
Error.Code="خطا: %1"
Error.URL="نشانی: %2"
Zoom.Reset="بازنشانی بزرگنمایی"
Zoom.Out="کوچکنمایی"
Zoom.In="بزرگنمایی"
ErrorCode.ERR_CONNECTION_REFUSED="سرور از اتصال خودداری کرد"
ErrorCode.ERR_NAME_NOT_RESOLVED="آدرس IP سرور یافت نشد"
ErrorCode.ERR_CONNECTION_TIMED_OUT="مهلت اتصال پایان یافت"
ErrorCode.ERR_TIMED_OUT="وقت انجام عملیات به پایان رسید"
ErrorCode.ERR_FILE_NOT_FOUND="پرونده پیدا نشد"
ErrorCode.ERR_FAILED="اتصال ناموفق بود"
ErrorCode.ERR_NETWORK_CHANGED="شبکه تغییر کرد"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="تداخل نگارش SSL. ممکن است TLS 1.3 تغییر یافته یا مسدود شده باشد."
ErrorCode.ERR_SSL_PROTOCOL_ERROR="خطای پروتکل SSL. نتوانستیم یک اتصال امن ایجاد کنیم."
ErrorCode.ERR_CERT_DATE_INVALID="گواهی SSL سرور قدیمی است یا زمان رایانه شما نادرست است."
07070100000049000081A400000000000000000000000168D5836B000008EC000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/fi-FI.iniLocalFile="Paikallinen tiedosto"
Width="Leveys"
Height="Korkeus"
CSS="Mukautettu CSS"
ShutdownSourceNotVisible="Sulje lähde kun se ei ole näkyvissä"
RefreshBrowserActive="Lataa selain uudelleen kun näkymä valitaan"
RefreshNoCache="Lataa nykyisen sivun välimuisti uudelleen"
BrowserSource="Selain"
CustomFrameRate="Käytä mukautettua ruutunopeutta"
RerouteAudio="Hallitse ääntä OBS:n kautta"
Inspect="Tarkista"
DevTools="Tarkista selaintelakka '%1'"
CopyUrl="Kopioi nykyinen osoite"
WebpageControlLevel="Sivun käyttöoikeudet"
WebpageControlLevel.Level.None="Ei pääsyä OBS:ään"
WebpageControlLevel.Level.ReadObs="Lukuoikeus OBS:n tilatietoihin"
WebpageControlLevel.Level.ReadUser="Lukuoikeus käyttäjätietoihin (nykyinen skene-kokoelma, siirtymät)"
WebpageControlLevel.Level.Basic="Perustason pääsy OBS:ään (tallenna toistopuskuri jne.)"
WebpageControlLevel.Level.Advanced="Edistynyt pääsy OBS:ään (Vaihda skenejä, käynnistä/pysäytä toistopuskuri jne.)"
WebpageControlLevel.Level.All="Täysi pääsy OBS:ään (Käynnistä/lopeta striimaaminen ilman varoitusta jne.)"
Dialog.Alert="Javascript-hälytys"
Dialog.Confirm="Javascript-vahvistus"
Dialog.Prompt="JavaScript-kehote"
Dialog.BrowserDock="Selaintelakka"
Dialog.ReceivedFrom="Vastaanotettu '%1'"
Error.Title="Sivua ei voitu lataa!"
Error.Description="Varmista, että osoite on oikea ja että sivustolla ei ole ongelmia."
Error.Retry="Napsauta tästä yrittääksesi uudelleen"
Error.Code="Virhe: %1"
Error.URL="URL-osoite: %2"
Zoom.Reset="Oletusnäkymä"
Zoom.Out="Loitonna"
Zoom.In="Lähennä"
ErrorCode.ERR_CONNECTION_REFUSED="Palvelin hylkäsi yhteyden"
ErrorCode.ERR_NAME_NOT_RESOLVED="Palvelimen IP-osoitetta ei löydy"
ErrorCode.ERR_CONNECTION_TIMED_OUT="Yhteys aikakatkaistiin"
ErrorCode.ERR_TIMED_OUT="Toiminto aikakatkaistiin"
ErrorCode.ERR_FILE_NOT_FOUND="Tiedostoa ei löydy"
ErrorCode.ERR_FAILED="Yhdistäminen epäonnistui"
ErrorCode.ERR_NETWORK_CHANGED="Verkko vaihtui"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="SSL-version häiriö. TLS 1.3 saattaa olla estetty tai muutettu."
ErrorCode.ERR_SSL_PROTOCOL_ERROR="SSL-protokollavirhe. Suojattua yhteyttä ei voitu muodostaa."
ErrorCode.ERR_CERT_DATE_INVALID="Palvelimen SSL-varmenne on vanhentunut tai tietokoneesi aika on virheellinen."
0707010000004A000081A400000000000000000000000168D5836B00000830000000000000000000000000000000000000004C00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/fil-PH.iniLocalFile="Lokal na payl"
Width="Ang Lapad"
Height="Ang Taas"
CSS="Sinadya sa CSS"
ShutdownSourceNotVisible="Patayin ang pinagmulan kapag hindi nakikita"
RefreshBrowserActive="Sariwain ang browser kapag naging aktibo ang eksena"
RefreshNoCache="Sariwain ang cache ng tinututukang pahina"
CustomFrameRate="Gamitin ang pasadyang frame rate"
RerouteAudio="I-kontrol ang dami gamit ang OBS"
Inspect="Suriin"
DevTools="Suriin Ang Browser Dock'%1'"
WebpageControlLevel="Mga permiso sa pahina"
WebpageControlLevel.Level.None="Walang access sa OBS"
WebpageControlLevel.Level.ReadObs="Basahin ang access sa impormasyon ng katayuan ng OBS"
WebpageControlLevel.Level.ReadUser="Basahin ang access sa impormasyon ng user (kasalukuyang Scene Collection, Transitions)"
WebpageControlLevel.Level.Basic="Pangunahing access sa OBS (I-save ang replay buffer, atbp.)"
WebpageControlLevel.Level.Advanced="Advanced na access sa OBS (Baguhin ang mga eksena, Start/Stop replay buffer, atbp.)"
WebpageControlLevel.Level.All="Buong pag-access sa OBS (Magsimula/Tumigil sa pag-stream ng walang babala, atbp.)"
Dialog.Confirm="Kumpirmahin ang JavaScript"
Dialog.ReceivedFrom="Natanggap mula sa '%1'"
Error.Title="Hindi ma-load ang pahina!"
Error.Description="Tiyaking na tama ang address, at walang mga isyu ang site."
Error.Retry="Pumindot dito para subukan muli"
Error.Code="Kamalian: %1"
ErrorCode.ERR_CONNECTION_REFUSED="Hindi tinanggap ng server ang koneksyon"
ErrorCode.ERR_NAME_NOT_RESOLVED="Hindi matagpuan ang IP address ng server"
ErrorCode.ERR_CONNECTION_TIMED_OUT="Umantala ang koneksyon"
ErrorCode.ERR_TIMED_OUT="Nag-time out ang operasyon"
ErrorCode.ERR_FILE_NOT_FOUND="Hindi natagpuan ang file"
ErrorCode.ERR_FAILED="Bigong kumunekta"
ErrorCode.ERR_NETWORK_CHANGED="Naiba ang network"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="Panghihimasok sa bersyon ng SSL. Maaaring ma-block o mabago ang TLS 1.3."
ErrorCode.ERR_SSL_PROTOCOL_ERROR="Error sa SSL protocol. Hindi makagawa ng secure na koneksyon."
ErrorCode.ERR_CERT_DATE_INVALID="Luma na ang SSL certificate ng server o hindi tama ang oras ng iyong computer."
0707010000004B000081A400000000000000000000000168D5836B000009A9000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/fr-FR.iniLocalFile="Fichier local"
Width="Largeur"
Height="Hauteur"
FPS="Images par seconde"
CSS="CSS personnalisé"
ShutdownSourceNotVisible="Désactiver la source quand elle n'est pas visible"
RefreshBrowserActive="Rafraîchir le navigateur lorsque la scène devient active"
RefreshNoCache="Rafraîchir le cache de cette page"
BrowserSource="Navigateur web"
CustomFrameRate="Définir manuellement le débit d'images (FPS)"
RerouteAudio="Contrôler l'audio via OBS"
Inspect="Inspecter"
DevTools="Inspecter le dock du navigateur '%1'"
CopyUrl="Copier l'adresse actuelle"
WebpageControlLevel="Permissions de la page"
WebpageControlLevel.Level.None="Aucun accès à OBS"
WebpageControlLevel.Level.ReadObs="Accès en lecture aux informations d'état OBS"
WebpageControlLevel.Level.ReadUser="Accès en lecture aux informations utilisateur (Collection de scène actuelle, Transitions)"
WebpageControlLevel.Level.Basic="Accès basique à OBS (enregistrer le tampon de relecture, etc.)"
WebpageControlLevel.Level.Advanced="Accès avancé à OBS (Changement de scène, Démarrer/Arrêter le tampon de relecture, etc.)"
WebpageControlLevel.Level.All="Accès complet à OBS (Démarrer/Arrêter le stream sans avertissement, etc.)"
Dialog.Alert="Alerte Javascript"
Dialog.Confirm="Confirmation Javascript"
Dialog.Prompt="Saisie JavaScript"
Dialog.BrowserDock="Dock du navigateur"
Dialog.ReceivedFrom="Reçu de '%1'"
Error.Title="Impossible de charger cette page !"
Error.Description="Veuillez vérifier que l'adresse est correcte et que le site n'a pas de problèmes."
Error.Retry="Cliquer ici pour réessayer"
Error.Code="Erreur : %1"
Error.URL="URL : %2"
Zoom.Reset="Réinitialiser le zoom"
Zoom.Out="Dézoomer"
Zoom.In="Zoomer"
ErrorCode.ERR_CONNECTION_REFUSED="Le serveur a refusé la connexion"
ErrorCode.ERR_NAME_NOT_RESOLVED="L'adresse IP du serveur est introuvable"
ErrorCode.ERR_CONNECTION_TIMED_OUT="Délai de connexion dépassé"
ErrorCode.ERR_TIMED_OUT="Délai de connexion dépassé"
ErrorCode.ERR_FILE_NOT_FOUND="Fichier introuvable"
ErrorCode.ERR_FAILED="Échec de connexion"
ErrorCode.ERR_NETWORK_CHANGED="Changement de réseau"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="Interférence de version SSL. TLS 1.3 est probablement bloqué ou altéré."
ErrorCode.ERR_SSL_PROTOCOL_ERROR="Erreur de protocole SSL. Impossible d'établir une connexion sécurisée."
ErrorCode.ERR_CERT_DATE_INVALID="Le certificat SSL du serveur est expiré ou l'heure de votre ordinateur est incorrecte."
0707010000004C000081A400000000000000000000000168D5836B00000718000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/gd-GB.iniLocalFile="Faidhle ionadail"
Width="Leud"
Height="Àirde"
CSS="CSS gnàthaichte"
ShutdownSourceNotVisible="Dùin sìos an tùs mura faicear e"
RefreshBrowserActive="Ath-nuadhaich am brabhsair nuair a ghnìomhaicheas an t-sealladh"
RefreshNoCache="Ath-nuadhaich tasgadan na duilleige làithrich"
BrowserSource="Brabhsair"
CustomFrameRate="Cleachd reat fhrèamaichean gnàthaichte"
RerouteAudio="Stiùirich an fhuaim le OBS"
WebpageControlLevel="Ceadan na duilleige"
WebpageControlLevel.Level.None="Gun inntrigeadh dha OBS"
WebpageControlLevel.Level.Basic="Inntrigeadh bunasach dha OBS (Sàbhail bufair na h-ath-chluiche is msaa.)"
WebpageControlLevel.Level.Advanced="Inntrigeadh adhartach dha OBS (atharraich seallaidhean, tòisich/cuir stad air bufair na h-ath-chluiche is msaa.)"
WebpageControlLevel.Level.All="Làn-inntrigeadh dha OBS (tòisich/cuir stad air sruthadh gun rabhadh is msaa.)"
Error.Title="Cha b’ urrainn dhuinn an duilleag ud a luchdadh!"
Error.Description="Dèan cinnteach gu bheil an seòladh mar bu chòir ’s nach eil duilgheadasan aig an làrach."
Error.Retry="Briog an-seo airson fheuchainn ris a-rithist"
Error.Code="Mearachd: %1"
ErrorCode.ERR_CONNECTION_REFUSED="Dhiùlt am frithealaiche an ceangal"
ErrorCode.ERR_NAME_NOT_RESOLVED="Cha deach seòladh IP an fhrithealaiche a lorg"
ErrorCode.ERR_CONNECTION_TIMED_OUT="Dh'fhalbh an ùine air a' cheangal"
ErrorCode.ERR_FILE_NOT_FOUND="Cha deach am faidhle a lorg"
ErrorCode.ERR_FAILED="Cha deach leinn ceangal a dhèanamh"
ErrorCode.ERR_NETWORK_CHANGED="Dh’atharraich an lìonra"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="Teagmhail mun tionndadh SSL. Dh’fhaoidte gu bheil TLS 1.3 ’ga bhacadh no air atharrachadh."
ErrorCode.ERR_SSL_PROTOCOL_ERROR="Mearachd pròtacail SSL. Cha b’ urrainn dhuinn ceangal tèarainte a stèidheachadh."
0707010000004D000081A400000000000000000000000168D5836B00000919000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/gl-ES.iniLocalFile="Ficheiro local"
Width="Largo"
Height="Alto"
CSS="CSS personalizado"
ShutdownSourceNotVisible="Apagar a fonte cando non sexa visíbel"
RefreshBrowserActive="Actualizar o navegador cando se active a escena"
RefreshNoCache="Actualizar a caché da páxina actual"
BrowserSource="Navegador"
CustomFrameRate="Usar unha taxa de fotogramas personalizada"
RerouteAudio="Controlar o son mediante OBS"
Inspect="Inspeccionar"
DevTools="Inspeccionar o panel do navegador '%1'"
CopyUrl="Copiar a dirección actual"
WebpageControlLevel="Paxina de permisos"
WebpageControlLevel.Level.None="Non hai acceso a OBS"
WebpageControlLevel.Level.ReadObs="Acceder á información do estado de OBS"
WebpageControlLevel.Level.ReadUser="Acceder á información do usuario (colección de escenas actuais, transicións)"
WebpageControlLevel.Level.Basic="Acceso básico a OBS (Gardar memoria interna, etc)"
WebpageControlLevel.Level.Advanced="Acceso avanzado a OBS (trocar fontes, Comezar/Deter memoria intermedia, etc..)"
WebpageControlLevel.Level.All="Acceso completo a OBS (Comezar/Parar fluxo online con avisos, etc.)"
Dialog.Alert="Alarma JavaScript"
Dialog.Confirm="Confirmar JavaScript"
Dialog.Prompt="Solicitude de JavaScript"
Dialog.BrowserDock="Base do navegador"
Dialog.ReceivedFrom="Recibido dende '% 1'"
Error.Title="Non se pode cargar a páxina!"
Error.Description="Asegúrate de que o enderezo é correcto e de que o sitio non teña problemas."
Error.Retry="Prema aqui para reintentar"
Error.Code="Erro: %1"
Zoom.Reset="Restablecer Zoom"
Zoom.Out="Reducir"
Zoom.In="Ampliar"
ErrorCode.ERR_CONNECTION_REFUSED="Conexión rexeitada polo servidor"
ErrorCode.ERR_NAME_NOT_RESOLVED="Non se atopa a dirección IP do Servidor"
ErrorCode.ERR_CONNECTION_TIMED_OUT="Tempo de conexión esgotada"
ErrorCode.ERR_TIMED_OUT="A operación esgotou o tempo"
ErrorCode.ERR_FILE_NOT_FOUND="Non se atopa o ficheiro"
ErrorCode.ERR_FAILED="Erro na conexión"
ErrorCode.ERR_NETWORK_CHANGED="Cambio de rede"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="Interferencia da versión SSL. É posible que TLS 1.3 estea bloqueado ou modificado."
ErrorCode.ERR_SSL_PROTOCOL_ERROR="Erro no protocolo SSL. Non se pode crear unha conexión segura."
ErrorCode.ERR_CERT_DATE_INVALID="O certificado SSL do servidor está desactualizado ou a hora do teu ordenador é incorrecta."
0707010000004E000081A400000000000000000000000168D5836B00000AC3000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/he-IL.iniLocalFile="קובץ מקומי"
URL="כתובת"
Width="רוחב"
Height="גובה"
CSS="CSS מותאם אישית"
ShutdownSourceNotVisible="כיבוי המקור כאשר אינו גלוי"
RefreshBrowserActive="ריענון הדפדפן בעת הפעלת הסצנה"
RefreshNoCache="ריענון זיכרון מטמון של העמוד הנוכחי"
BrowserSource="דפדפן"
CustomFrameRate="השתמש בקצב פריימים בהתאמה אישית"
RerouteAudio="בקרת שמע דרך OBS"
Inspect="חקירה"
DevTools="חקירת מעגן הדפדפן ‚%1’"
CopyUrl="העתקת הכתובת הנוכחית"
WebpageControlLevel="הרשאות דף"
WebpageControlLevel.Level.None="אין גישה ל-OBS"
WebpageControlLevel.Level.ReadObs="גישת קריאה לפרטי המצב של OBS"
WebpageControlLevel.Level.ReadUser="גישת קריאה לפרטי המשתמש (אוסף סצנות נוכחי, מעברונים)"
WebpageControlLevel.Level.Basic="גישה בסיסית אל OBS (שמירת מטמון ההשמעה החוזרת, וכו׳)"
WebpageControlLevel.Level.Advanced="גישה מתקדמת אל OBS (שינוי סצינות, הפעלה/כיבוי של מטמון ההשמעה החוזרת, וכו׳)"
WebpageControlLevel.Level.All="גישה מלאה ל-OBS (התחלת/הפסקת שידור חי ללא אזהרה, וכו')"
Dialog.Alert="התראת JavaScript"
Dialog.Confirm="אישור JavaScript"
Dialog.Prompt="בקשה מ־JavaScript"
Dialog.BrowserDock="מעגן דפדפן"
Dialog.ReceivedFrom="התקבל מ־‚%1’"
Error.Title="לא היה ניתן לטעון את העמוד!"
Error.Description="יש לוודא שהכתובת נכונה ושלא קיימות בעיות באתר."
Error.Retry="יש ללחוץ כאן כדי לנסות שוב"
Error.Code="שגיאה: %1"
Error.URL="כתובת: %2"
Zoom.Reset="איפוס זום"
Zoom.Out="הקטנת תצוגה"
Zoom.In="הגדלת תצוגה"
ErrorCode.ERR_CONNECTION_REFUSED="השרת סירב לחיבור"
ErrorCode.ERR_NAME_NOT_RESOLVED="כתובת ה־IP של השרת לא נמצאה"
ErrorCode.ERR_CONNECTION_TIMED_OUT="זמן החיבור פג"
ErrorCode.ERR_TIMED_OUT="הזמן שהוקצה לפעילות תם"
ErrorCode.ERR_FILE_NOT_FOUND="הקובץ לא נמצא"
ErrorCode.ERR_FAILED="ההתחברות נכשלה"
ErrorCode.ERR_NETWORK_CHANGED="רשת השתנתה"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="הפרעה לגרסת SSL. TLS 1.3 עשוי להיות חסום או ערוך."
ErrorCode.ERR_SSL_PROTOCOL_ERROR="שגיאת פרוטוקול SSL. לא ניתן היה ליצור חיבור מאובטח"
ErrorCode.ERR_CERT_DATE_INVALID="אישור ה־SSL של השרת אינו בתוקף או שהשעה של המחשב שלך שגויה"
0707010000004F000081A400000000000000000000000168D5836B00000FEF000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/hi-IN.iniLocalFile="स्थानीय फ़ाइल"
Width="चौड़ाई"
Height="ऊँचाई"
CSS="निर्मित CSS"
ShutdownSourceNotVisible="स्रोत जब ओझल हो तो उसे बंद कर दें"
RefreshBrowserActive="दृश्य सक्रिय होने पर ब्राउज़र रिफ़्रेश करें"
RefreshNoCache="वर्तमान पृष्ठ का कैश ताज़ा करें"
BrowserSource="ब्राउज़र"
CustomFrameRate="निर्मित फ़्रेम रेट का प्रयोग करें"
RerouteAudio="ऑडियो OBS के माध्यम से नियंत्रित करें"
Inspect="निरीक्षण करें"
DevTools="ब्राउज़र डॉक '%1' का निरीक्षण करें"
CopyUrl="वर्तमान पता कॉपी करें"
WebpageControlLevel="पृष्ठ की अनुमतियां"
WebpageControlLevel.Level.None="OBS तक पहुँच नहीं है"
WebpageControlLevel.Level.ReadObs="OBS की स्थिति की जानकारी पढ़ने के लिए अधिकृत"
WebpageControlLevel.Level.ReadUser="उपयोगकर्ता जानकारी पढ़ने की पहुँच (वर्तमान दृश्य संग्रह, संक्रांति)"
WebpageControlLevel.Level.Basic="OBS तक बुनियादी पहुंच (रिप्ले बफर सहेजें, आदि )"
WebpageControlLevel.Level.Advanced="OBS के लिए उन्नत पहुंच (दृश्य बदलें, रीप्ले बफर प्रारंभ/बंद करें, आदि)"
WebpageControlLevel.Level.All="OBS तक पूर्ण पहुंच (बिना किसी चेतावनी के स्ट्रीमिंग शुरू/बंद करें, आदि)"
Dialog.Alert="जावास्क्रिप्ट चेतावनी"
Dialog.Confirm="जावास्क्रिप्ट पुष्टिकरण"
Dialog.Prompt="जावास्क्रिप्ट संवाद"
Dialog.BrowserDock="ब्राउज़र डॉक"
Dialog.ReceivedFrom="'%1' से प्राप्त"
Error.Title="उस पेज को लोड नहीं किया जा सका!"
Error.Description="सुनिश्चित करें कि पता सही है और साइट में कोई समस्या नहीं है."
Error.Retry="पुनः प्रयास करने के लिए क्लिक करें"
Error.Code="त्रुटि : %1"
Zoom.Reset="ज़ूम रीसेट करें"
Zoom.Out="जूम आउट करें"
Zoom.In="छोटा करें"
ErrorCode.ERR_CONNECTION_REFUSED="सर्वर ने कनेक्शन से इनकार कर दिया"
ErrorCode.ERR_NAME_NOT_RESOLVED="सर्वर का IP पता नहीं मिला"
ErrorCode.ERR_CONNECTION_TIMED_OUT="कनेक्शन टाइम आउट"
ErrorCode.ERR_TIMED_OUT="ऑपरेशन टाइम आउट"
ErrorCode.ERR_FILE_NOT_FOUND="फ़ाइल नहीं मिली"
ErrorCode.ERR_FAILED="जुड़ने में विफल"
ErrorCode.ERR_NETWORK_CHANGED="नेटवर्क बदल गया"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="SSL संस्करण हस्तक्षेप. TLS 1.3 को अवरुद्ध या संशोधित किया जा सकता है."
ErrorCode.ERR_SSL_PROTOCOL_ERROR="SSL प्रोटोकॉल त्रुटि. सुरक्षित कनेक्शन नहीं बना सका."
ErrorCode.ERR_CERT_DATE_INVALID="सर्वर का SSL प्रमाणपत्र पुराना है या आपके कंप्यूटर का समय गलत है."
07070100000050000081A400000000000000000000000168D5836B0000092C000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/hr-HR.iniLocalFile="Lokalna datoteka"
URL="Web adresa"
Width="Širina"
Height="Visina"
CSS="Prilagođeni CSS"
ShutdownSourceNotVisible="Isključiti izvor kada nije vidljiv"
RefreshBrowserActive="Osvježi preglednik kada scena postane aktivna"
RefreshNoCache="Osvježi predmemoriju trenutne stranice"
BrowserSource="Preglednik"
CustomFrameRate="Koristite prilagođenu brzinu kadrova"
RerouteAudio="Kontrolirajte zvuk preko OBS-a"
Inspect="Provjeri"
DevTools="Pregledajte Dock preglednika '%1'"
CopyUrl="Kopirajte trenutačnu adresu"
WebpageControlLevel="Dozvole stranice"
WebpageControlLevel.Level.None="Bez pristupa OBS-u"
WebpageControlLevel.Level.ReadObs="Pristup čitanju informacija stanja OBS-a"
WebpageControlLevel.Level.ReadUser="Pristup za čitanje korisničkih podataka (trenutna kolekcija scena, prijelazi)"
WebpageControlLevel.Level.Basic="Osnovni pristup OBS-u (spremanje međuspremnika za reprodukciju itd.)"
WebpageControlLevel.Level.Advanced="Napredni pristup u OBS (Promjena scena, međumemorija za Pokreni/Zaustavi reprodukcije, itd.)"
WebpageControlLevel.Level.All="Puni pristup OBS-u (Pokretanje/zaustavljanje streaminga bez upozorenja itd.)"
Dialog.Alert="JavaScript upozorenje"
Dialog.Confirm="JavaScript potvrda"
Dialog.Prompt="JavaScript upit"
Dialog.BrowserDock="Dock preglednika"
Dialog.ReceivedFrom="Primljeno od '%1'"
Error.Title="Stranica se nije mogla učitati!"
Error.Description="Provjeri je li adresa točna i je li stranica sadrži probleme."
Error.Retry="Pritisni ovdje za ponovni pokušaj"
Error.Code="Greška: %1"
Zoom.Reset="Ponovno postavi uvećanje"
Zoom.Out="Smanji"
Zoom.In="Povećaj"
ErrorCode.ERR_CONNECTION_REFUSED="Server je odbio vezu"
ErrorCode.ERR_NAME_NOT_RESOLVED="IP adresa servera nije pronađena"
ErrorCode.ERR_CONNECTION_TIMED_OUT="Vrijeme veze je isteklo"
ErrorCode.ERR_TIMED_OUT="Vrijeme operacije je isteklo"
ErrorCode.ERR_FILE_NOT_FOUND="Datoteka nije pronađena"
ErrorCode.ERR_FAILED="Neuspjelo povezivanje"
ErrorCode.ERR_NETWORK_CHANGED="Mreža se promijenila"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="Interferencija SSL verzije. TLS 1.3 bi mogao biti blokiran ili modificiran."
ErrorCode.ERR_SSL_PROTOCOL_ERROR="Pogreška SSL protokola. Nije moguće uspostaviti sigurnu vezu."
ErrorCode.ERR_CERT_DATE_INVALID="SSL certifikat poslužitelja je zastario ili je vrijeme na vašem računalu netočno."
07070100000051000081A400000000000000000000000168D5836B000009EF000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/hu-HU.iniLocalFile="Helyi fájl"
URL="Webcím"
Width="Szélesség"
Height="Magasság"
CSS="Egyéni CSS"
ShutdownSourceNotVisible="Lekapcsolás, ha nem látható"
RefreshBrowserActive="Böngésző frissítése ha aktívvá válik"
RefreshNoCache="Aktuális oldal gyorsítótárának frissítése"
BrowserSource="Böngésző"
CustomFrameRate="Egyéni képkockaráta használata"
RerouteAudio="Hangvezérlés OBS-en keresztül"
Inspect="Vizsgálat"
DevTools="A(z) „%1” böngésződokk vizsgálata"
CopyUrl="Jelenlegi cím másolása"
WebpageControlLevel="Oldal engedélyei"
WebpageControlLevel.Level.None="Nincs hozzáférés az OBS-hez"
WebpageControlLevel.Level.ReadObs="Olvasási hozzáférés az OBS állapotinformációihoz"
WebpageControlLevel.Level.ReadUser="Olvasási hozzáférés a felhasználói információkhoz (jelenlegi jelenetgyűjtemény, átmenetek)"
WebpageControlLevel.Level.Basic="Alapvető hozzáférés az OBS-hez (visszajátszási puffer mentése, stb.)"
WebpageControlLevel.Level.Advanced="Speciális hozzáférés az OBS-hez (Jelenetek váltása, lejátszási puffer indítása/leállítása, stb.)"
WebpageControlLevel.Level.All="Teljes hozzáférés az OBS-hez (közvetítés figyelmeztetés nélküli indítása/leállítása, stb.)"
Dialog.Alert="JavaScript figyelmeztetés"
Dialog.Confirm="Javascript megerősítés"
Dialog.Prompt="JavaScript kérdés"
Dialog.BrowserDock="Böngésződokk"
Dialog.ReceivedFrom="Fogadva innen: „%1”"
Error.Title="Az oldal nem tölthető be."
Error.Description="Győződjön meg róla, hogy a cím helyes, és hogy nincsenek problémák az oldallal."
Error.Retry="Újrapróbálkozáshoz kattintson ide"
Error.Code="Hiba: %1"
Zoom.Reset="Nagyítás visszaállítása"
Zoom.Out="Kicsinyités"
Zoom.In="Nagyítás"
ErrorCode.ERR_CONNECTION_REFUSED="A kiszolgáló megtagadta a kapcsolatot"
ErrorCode.ERR_NAME_NOT_RESOLVED="A kiszolgáló IP-címe nem található"
ErrorCode.ERR_CONNECTION_TIMED_OUT="Kapcsolódási időtúllépés"
ErrorCode.ERR_TIMED_OUT="A művelet túllépte az időkorlátot"
ErrorCode.ERR_FILE_NOT_FOUND="A fájl nem található"
ErrorCode.ERR_FAILED="Csatlakozás sikertelen"
ErrorCode.ERR_NETWORK_CHANGED="Hálózat megváltozott"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="SSL verzióprobléma. A TLS 1.3 lehet hogy blokkolt vagy módosított."
ErrorCode.ERR_SSL_PROTOCOL_ERROR="SSL protokollhiba. A biztonságos kapcsolat nem hozható létre."
ErrorCode.ERR_CERT_DATE_INVALID="A kiszolgáló SSL tanúsítványa elévült, vagy a számítógép órája helytelen."
07070100000052000081A400000000000000000000000168D5836B00000D09000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/hy-AM.iniLocalFile="Տեղային ֆայլ"
URL="URL հասցե"
Width="Լայնություն"
Height="Բարձրություն"
FPS="ԿՄՎ"
CSS="CSS"
ShutdownSourceNotVisible="Անջատել, երբ տեսանելի չէ"
RefreshBrowserActive="Թարմացրեք դիտարկիչը, երբ տեսարանը ակտիվանա"
RefreshNoCache="Թարմացրեք ընթացիկ էջի քեշը"
BrowserSource="Բրաուզեր"
CustomFrameRate="Օգտագործեք հատուկ շրջանակի արագություն"
RerouteAudio="Աուդիո կառավարում OBS-ի միջոցով"
Inspect="Ստուգել"
DevTools="Ստուգել դիտարկչի դոկը '%1'"
CopyUrl="Պատճենել ընթացիկ հասցեն"
WebpageControlLevel="Էջի լուծում"
WebpageControlLevel.Level.None="OBS մուտք չկա"
WebpageControlLevel.Level.ReadObs="OBS կարգավիճակի մասին տեղեկությունները կարդալու հասանելիություն"
WebpageControlLevel.Level.ReadUser="Կարդացեք օգտվողի տեղեկատվության հասանելիությունը (ընթացիկ տեսարանի հավաքածու, անցումներ)"
WebpageControlLevel.Level.Basic="Հիմնական մուտք դեպի OBS (Պահպանել վիդեո բուֆերը և այլն)"
WebpageControlLevel.Level.Advanced="Ընդլայնված մուտք դեպի OBS (Փոխել տեսարանները, Սկսել/Դադարեցնել ձայնագրման բուֆերը և այլն)"
WebpageControlLevel.Level.All="Ամբողջական մուտք դեպի OBS (Սկսել/Դադարեցնել ուղիղ եթեր առանց նախազգուշացման և այլն)"
Dialog.Alert="JavaScript-ի նախազգուշացում"
Dialog.Confirm="JavaScript վավերացում"
Dialog.Prompt="JavaScript գործիքի հուշում"
Dialog.BrowserDock="Դոք-հսկիչի բրաուզռրը"
Dialog.ReceivedFrom="Ստացված է «%1»-ից"
Error.Title="Չհաջողվեց բեռնել այս էջը:"
Error.Description="Ստուգեք, որ հասցեն ճիշտ է, և արդյոք կայքը հասանելի է:"
Error.Retry="Սեղմեք այստեղ՝ կրկին փորձելու համար"
Error.Code="Սխալ: %1"
Error.URL="URL-հասցե: %2"
Zoom.Reset="Վերականգնել սանդղակը"
Zoom.Out="Փոքրացնել"
Zoom.In="Մեծացնել"
ErrorCode.ERR_CONNECTION_REFUSED="Սերվերը մերժել է կապը"
ErrorCode.ERR_NAME_NOT_RESOLVED="Սերվերի IP հասցեն չի գտնվել"
ErrorCode.ERR_CONNECTION_TIMED_OUT="Միացման ժամանակը սպառվել է"
ErrorCode.ERR_TIMED_OUT="Գործողության ժամանակը լրացավ"
ErrorCode.ERR_FILE_NOT_FOUND="Ֆայլը չի գտնվել"
ErrorCode.ERR_FAILED="Չհաջողվեց միանալ"
ErrorCode.ERR_NETWORK_CHANGED="Ցանցը փոխվել է"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="SSL տարբերակի խախտում: Թերևս TLS 1.3-ն արգելափակված է կամ փոփոխված:"
ErrorCode.ERR_SSL_PROTOCOL_ERROR="SSL արձանագրության սխալ: Չհաջողվեց անվտանգ կապ հաստատել:"
ErrorCode.ERR_CERT_DATE_INVALID="Սերվերի SSL վկայագիրը հնացած է կամ ձեր համակարգչի ժամանակը սխալ է:"
07070100000053000081A400000000000000000000000168D5836B0000089E000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/id-ID.iniLocalFile="Berkas lokal"
Width="Lebar"
Height="Tinggi"
CSS="Sesuaikan CSS"
ShutdownSourceNotVisible="Nonaktifkan sumber ketika tidak terlihat"
RefreshBrowserActive="Muat ulang peramban ketika adegan menjadi aktif"
RefreshNoCache="Muat ulang cache halaman saat ini"
BrowserSource="Peramban"
CustomFrameRate="Gunakan modifikasi frame rate"
RerouteAudio="Kontrol audio melalui OBS"
Inspect="Inspeksi"
DevTools="Inspeksi Dok Peramban '%1'"
CopyUrl="Salin alamat saat ini"
WebpageControlLevel="Izin halaman"
WebpageControlLevel.Level.None="Tidak ada akses ke OBS"
WebpageControlLevel.Level.ReadObs="Akses membaca ke informasi status OBS"
WebpageControlLevel.Level.ReadUser="Akses membaca ke informasi pengguna (Koleksi Adegan saat ini, Transisi)"
WebpageControlLevel.Level.Basic="Akses dasar ke OBS (Menyimpan buffer replay, dll.)"
WebpageControlLevel.Level.Advanced="Akses lanjutan ke OBS (Mengubah adegan, Mulai/Berhenti buffer replay, dll.)"
WebpageControlLevel.Level.All="Akses penuh ke OBS (Mulai/Berhenti streaming tanpa peringatan, dll.)"
Dialog.Alert="Peringatan JavaScript"
Dialog.Confirm="Konfirmasi JavaScript"
Dialog.Prompt="Perintah JavaScript"
Dialog.BrowserDock="Dok Peramban"
Dialog.ReceivedFrom="Diterima dari '%1'"
Error.Title="Tidak dapat memuat halaman itu!"
Error.Description="Pastikan alamat situsnya benar, dan situs tersebut sedang tidak mengalami masalah."
Error.Retry="Klik disini untuk mencoba kembali"
Error.Code="Galat: %1"
Zoom.Reset="Atur Ulang Perbesaran"
Zoom.Out="Perkecil"
Zoom.In="Perbesar"
ErrorCode.ERR_CONNECTION_REFUSED="Server menolak koneksi"
ErrorCode.ERR_NAME_NOT_RESOLVED="Alamat IP server tidak ditemukan"
ErrorCode.ERR_CONNECTION_TIMED_OUT="Waktu koneksi habis"
ErrorCode.ERR_TIMED_OUT="Waktu pengoperasian habis"
ErrorCode.ERR_FILE_NOT_FOUND="Berkas tidak ditemukan"
ErrorCode.ERR_FAILED="Gagal terhubung"
ErrorCode.ERR_NETWORK_CHANGED="Jaringan berubah"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="Gangguan versi SSL. TLS 1.3 mungkin terblokir atau dimodifikasi."
ErrorCode.ERR_SSL_PROTOCOL_ERROR="Galat protokol SSL. Tidak dapat melakukan sambungan aman."
ErrorCode.ERR_CERT_DATE_INVALID="Sertifikat server SSL sudah usang atau jam pada komputer Anda tidak benar."
07070100000054000081A400000000000000000000000168D5836B00000916000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/it-IT.iniLocalFile="File locale"
Width="Larghezza"
Height="Altezza"
FPS="Fotogrammi al secondo"
CSS="CSS personalizzato"
ShutdownSourceNotVisible="Chiudi la fonte quando non è visibile"
RefreshBrowserActive="Ricarica la pagina del browser quando la scena diventa attiva"
RefreshNoCache="Aggiorna la cache della pagina attuale"
CustomFrameRate="Utilizza una velocità dei fotogrammi personalizzata"
RerouteAudio="Controlla audio tramite OBS"
Inspect="Ispeziona"
DevTools="Ispeziona dock del browser '%1'"
CopyUrl="Copia indirizzo attuale"
WebpageControlLevel="Permessi pagina"
WebpageControlLevel.Level.None="Nessun accesso a OBS"
WebpageControlLevel.Level.ReadObs="Accesso in lettura alle informazioni sullo stato di OBS"
WebpageControlLevel.Level.ReadUser="Accesso in lettura alle informazioni dell'utente (l'attuale collezione di scene, transizioni)"
WebpageControlLevel.Level.Basic="Accesso base a OBS (Salva il replay buffer, ecc.)"
WebpageControlLevel.Level.Advanced="Accesso avanzato a OBS (Cambio scene, Avvia/Ferma replay buffer, ecc.)"
WebpageControlLevel.Level.All="Accesso completo a OBS (Avvia/Ferma streaming senza preavviso, ecc.)"
Dialog.Alert="Avviso JavaScript"
Dialog.Confirm="Conferma JavaScript"
Dialog.Prompt="Richiesta Javascript"
Dialog.BrowserDock="Pannello Browser"
Dialog.ReceivedFrom="Ricevuto da '%1'"
Error.Title="Impossibile caricare quella pagina!"
Error.Description="Assicurati che l'indirizzo sia corretto e che il sito non abbia problemi."
Error.Retry="Clicca qui per riprovare"
Error.Code="Errore: %1"
Zoom.Reset="Ripristina zoom"
Zoom.Out="Zoom -"
Zoom.In="Zoom +"
ErrorCode.ERR_CONNECTION_REFUSED="Il server ha rifiutato la connessione"
ErrorCode.ERR_NAME_NOT_RESOLVED="Indirizzo IP del server non trovato"
ErrorCode.ERR_CONNECTION_TIMED_OUT="Connessione scaduta"
ErrorCode.ERR_TIMED_OUT="Timeout operazione"
ErrorCode.ERR_FILE_NOT_FOUND="File non trovato"
ErrorCode.ERR_FAILED="Impossibile connettersi"
ErrorCode.ERR_NETWORK_CHANGED="Rete modificata"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="Interferenza della versione SSL. TLS 1.3 potrebbe essere bloccato o modificato."
ErrorCode.ERR_SSL_PROTOCOL_ERROR="Errore del protocollo SSL. Impossibile creare una connessione sicura."
ErrorCode.ERR_CERT_DATE_INVALID="Il certificato SSL del server è obsoleto o data/ora del computer non sono corrette."
07070100000055000081A400000000000000000000000168D5836B00000A9A000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/ja-JP.iniLocalFile="ローカルファイル"
Width="幅"
Height="高さ"
CSS="カスタム CSS"
ShutdownSourceNotVisible="表示されていないときにソースをシャットダウンする"
RefreshBrowserActive="シーンがアクティブになったときにブラウザの表示を更新する"
RefreshNoCache="現在のページのキャッシュを更新"
BrowserSource="ブラウザ"
CustomFrameRate="カスタムフレームレートを使用する"
RerouteAudio="OBSで音声を制御する"
Inspect="検証"
DevTools="ブラウザドック '%1' を検証"
CopyUrl="現在のアドレスをコピー"
WebpageControlLevel="ページ権限"
WebpageControlLevel.Level.None="OBSへのアクセス権無し"
WebpageControlLevel.Level.ReadObs="OBSのステータス情報へのアクセス"
WebpageControlLevel.Level.ReadUser="ユーザー情報へのアクセス (現在のシーンコレクション、トランジション)"
WebpageControlLevel.Level.Basic="OBSへの基本的なアクセス (リプレイバッファの保存など。)"
WebpageControlLevel.Level.Advanced="OBSへの高度なアクセス (シーンの変更、リプレイバッファの開始/停止など。)"
WebpageControlLevel.Level.All="OBSへのフルアクセス (警告なしで配信を開始/停止するなど。)"
Dialog.Confirm="JavaScriptを確認"
Dialog.Prompt="JavaScriptプロンプト"
Dialog.BrowserDock="ブラウザドック"
Dialog.ReceivedFrom="受信元 '%1'"
Error.Title="そのページを読み込めませんでした!"
Error.Description="アドレスが正しいか、サイトに問題がないかを確認してください。"
Error.Retry="クリックして再試行"
Error.Code="エラー: %1"
Zoom.Reset="ズームをリセット"
Zoom.Out="ズームアウト"
Zoom.In="ズームイン"
ErrorCode.ERR_CONNECTION_REFUSED="サーバーが接続を拒否しました"
ErrorCode.ERR_NAME_NOT_RESOLVED="サーバーのIPアドレスが見つかりません"
ErrorCode.ERR_CONNECTION_TIMED_OUT="接続がタイムアウトしました"
ErrorCode.ERR_TIMED_OUT="操作がタイムアウトしました"
ErrorCode.ERR_FILE_NOT_FOUND="ファイルが見つかりません"
ErrorCode.ERR_FAILED="接続に失敗しました"
ErrorCode.ERR_NETWORK_CHANGED="ネットワークが変更されました"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="SSLバージョンの干渉。 TLS1.3がブロックまたは変更されている可能性があります。"
ErrorCode.ERR_SSL_PROTOCOL_ERROR="SSLプロトコルエラー。 安全な接続を確立できませんでした。"
ErrorCode.ERR_CERT_DATE_INVALID="サーバーのSSL証明書が古くなっているかコンピュータの時刻が正しくありません。"
07070100000056000081A400000000000000000000000168D5836B000010A0000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/ka-GE.iniLocalFile="ადგილობრივი ფაილი"
URL="ბმული"
Width="სიგანე"
Height="სიმაღლე"
CSS="მითითებული CSS"
ShutdownSourceNotVisible="დაიხუროს წყარო, როცა არაა ხილული"
RefreshBrowserActive="ბრაუზერის განახლება, სცენის ამოქმედებისას"
RefreshNoCache="მიმდინარე გვერდის მონაცემების განახლება"
BrowserSource="ვებბრაუზერი"
CustomFrameRate="მითითებული კადრის სიხშირე"
RerouteAudio="ხმის მართვა OBS-ით"
Inspect="გამოკვლევა"
DevTools="გამოკვლევა ბრაუზერის არეში „%1“"
CopyUrl="მიმდინარე მისამართის ასლი"
WebpageControlLevel="გვერდის ნებართვები"
WebpageControlLevel.Level.None="არაა წვდომა OBS-თან"
WebpageControlLevel.Level.ReadObs="წვდომა OBS-ის მდგომარეობის მონაცემებზე"
WebpageControlLevel.Level.ReadUser="წვდომა მომხმარებლის მონაცემებთან (მიმდინარე სცენი ნაკრები, გარდაქმნები)"
WebpageControlLevel.Level.Basic="გამარტივებული წვდომა OBS-თან (შენახვა, გადახვევა და ა.შ.)"
WebpageControlLevel.Level.Advanced="გაფართოებული წვდომა OBS-თან (სცენების გადართვა, გადახვევის ჩართვა/გამორთვა და ა.შ.)"
WebpageControlLevel.Level.All="სრული წვდომა OBS-თან (ნაკადის გაშვება/შეწყვეტა გაუფრთხილებლად და ა.შ.)"
Dialog.Alert="JavaScript-ის გაფრთხილება"
Dialog.Confirm="JavaScript-ის დასტური"
Dialog.Prompt="JavaScript-ის მითითება"
Dialog.BrowserDock="ვებგვერდის ნაწილი"
Dialog.ReceivedFrom="გამომგზავნი „%1“"
Error.Title="გვერდი ვერ ჩაიტვირთა!"
Error.Description="გადაამოწმეთ, მისამართის სისწორე და საიტის გამართულობა."
Error.Retry="გამეორებისთვის დაწკაპეთ აქ"
Error.Code="შეცდომა: %1"
Error.URL="ბმული: %2"
Zoom.Reset="საწყისი დაშორება"
Zoom.Out="დაშორება"
Zoom.In="მოახლოება"
ErrorCode.ERR_CONNECTION_REFUSED="სერვერმა უარყო კავშირი"
ErrorCode.ERR_NAME_NOT_RESOLVED="სერვერის IP-მისამართი ვერ მოიძებნა"
ErrorCode.ERR_CONNECTION_TIMED_OUT="კავშირის დრო ამოიწურა"
ErrorCode.ERR_TIMED_OUT="მოქმედების შესრულების დრო ამოიწურა"
ErrorCode.ERR_FILE_NOT_FOUND="ფაილი ვერ მოიძებნა"
ErrorCode.ERR_FAILED="დაკავშირება ვერ მოხერხდა"
ErrorCode.ERR_NETWORK_CHANGED="ქსელი შეიცვალა"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="SSL-ვერსიის გამო შეფერხება. TLS 1.3 შეზღუდული ან გადაკეთებულია."
ErrorCode.ERR_SSL_PROTOCOL_ERROR="SSL-ოქმის შეცდომა. დაცული კავშირი ვერ შედგა."
ErrorCode.ERR_CERT_DATE_INVALID="სერვერის SSL-სერტიფიკატი ვადაგასულია ან თქვენი კომპიუტერის საათია არეული."
07070100000057000081A400000000000000000000000168D5836B00000661000000000000000000000000000000000000004900000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/kaa.iniURL="URL mánzil"
Width="Enı"
Height="Uzınlıǵı"
FPS="Kadrlar tezligi (FPS)"
CSS="CSS"
RefreshBrowserActive="Saxna aktiv bolǵanda, brauzerdi jańalaw"
RefreshNoCache="Ámeldegi bet keshin jańalaw"
BrowserSource="Brauzer"
RerouteAudio="Audionı OBS arqalı basqarıw"
Inspect="tekseriw"
CopyUrl="Ámeldegi mánzildi kóshirip alıw"
WebpageControlLevel="Bet ruqsatları"
Dialog.Alert="JavaScript eskertiw"
Dialog.Confirm="JavaScript tastıyqlaw"
Dialog.ReceivedFrom="'%1' dan alındı"
Error.Title="Bul betti júklew ámelge aspadı!"
Error.Description="Mánzil durıs ekenligine hám saytta mashqala joqlıǵına isenim kámil etiń."
Error.Retry="Qayta urınıp kórıw ushın, usı jerge basıń"
Error.Code="Qátelik: %1"
Error.URL="URL mánzil: %2"
Zoom.Reset="Masshtabtı tiklew"
Zoom.Out="Masshtabtı kishireytiriw"
Zoom.In="Masshtabti úlkenlestiriw"
ErrorCode.ERR_CONNECTION_REFUSED="Server jalǵanıwdı biykarladı"
ErrorCode.ERR_NAME_NOT_RESOLVED="Serverdıń IP mánzili tabılmadı"
ErrorCode.ERR_CONNECTION_TIMED_OUT="Jalǵanıw waqtı tawsildi"
ErrorCode.ERR_TIMED_OUT="Operatsiya waqti tawsildi"
ErrorCode.ERR_FILE_NOT_FOUND="Fayl tawılmadı"
ErrorCode.ERR_FAILED="Jalǵanıw ámelge aspadı"
ErrorCode.ERR_NETWORK_CHANGED="Tarmaq ózgertildi"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="SSL versiyası mashqalası. TLS 1.3 bloklanǵan yamasa ózgertilgen bolıwı múmkin."
ErrorCode.ERR_SSL_PROTOCOL_ERROR="SSL protokolı qáteligi. Qáwipsiz jalǵanıwdı ornatıw múmkinshiligi bolmadı."
ErrorCode.ERR_CERT_DATE_INVALID="Serverdiń SSL sertifikatı eskirgen yamasa siziń kompyuterińizdegi sáne nadurıs."
07070100000058000081A400000000000000000000000168D5836B0000016E000000000000000000000000000000000000004D00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/kab-KAB.iniLocalFile="Afaylu adigan"
Width="Tehri"
Height="Tattayt"
CSS="CSS yugnen"
ShutdownSourceNotVisible="Sens aɣbalu ticki yettban"
RefreshBrowserActive="Sismeḍ iminig mi ara yeqel usayes d urmid"
RefreshNoCache="Sismeḍ tuffirt n usebter amiran"
BrowserSource="Iminig"
CustomFrameRate="Seqdec atug yugnen n yikataren"
RerouteAudio="Asenqed n umeslaw s ttawil n OBS"
07070100000059000081A400000000000000000000000168D5836B00000969000000000000000000000000000000000000004C00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/kmr-TR.iniLocalFile="Pelê herêmî"
Width="Pehnî"
Height="Bilindî"
CSS="CSS a kesanekirî"
ShutdownSourceNotVisible="Çavkaniyê rawestîne gava ku ne xuyabar be"
RefreshBrowserActive="Gava dîmen çalak bibe gerokê nû bike"
RefreshNoCache="Pêşbirka rûpela heyî nû bike"
BrowserSource="Gerok"
CustomFrameRate="Rêjeya çarçoveya ya kesanekirî bi kar bîne"
RerouteAudio="Kontrolkirina dengê bi riya OBS"
Inspect="Kontrol bike"
DevTools="Bendera '%1' kontrol bike"
CopyUrl="Navnîşana heyî jê bigire"
WebpageControlLevel="Mafên rûpelê"
WebpageControlLevel.Level.None="Gihîştina OBS tune ye"
WebpageControlLevel.Level.ReadObs="Gihîştina zanyariyên rewşa OBS bixwîne"
WebpageControlLevel.Level.ReadUser="Gihîştina zanyariyên bikarhêner bixwîne (Berhevoka dîmenan ya heyî, Derbasbûn)"
WebpageControlLevel.Level.Basic="Gihîştina bingehîn a OBS (Tomarkirina dubarekirina pêşbîra demkî, hwd.)"
WebpageControlLevel.Level.Advanced="Gihîştina pêşketî ya OBS-ê (Dîmenan biguherîne, Destpêkirin/Rawestandin dubarekirina pêşbîra demkî, hwd.)"
WebpageControlLevel.Level.All="Gihîştina tevahî ya OBS-ê (Bêyî hişyarî weşanê Destpê bike/Rawestîne, hwd.)"
Dialog.Alert="Hişyariya JavaScript"
Dialog.Confirm="Pejirandina JavaScript"
Dialog.Prompt="Daxwazkirina JavaScript"
Dialog.BrowserDock="Keştîgeha gerokê"
Dialog.ReceivedFrom="Ji '%1' wergirtî"
Error.Title="Nikarê wê rûpelê bar bike!"
Error.Description="Piştrast bike ku navnîşan rast e, û ku malper ne xwediyê pirsgirêkan e."
Error.Retry="Ji bo ji nû ve ceribandinê li vir bitike"
Error.Code="Çewtî: %1"
Zoom.Reset="Mezinkirinê ji nû be saz bike"
Zoom.Out="Dûr bixe"
Zoom.In="Nêzîk bike"
ErrorCode.ERR_CONNECTION_REFUSED="Rajekar pêwendî red kir"
ErrorCode.ERR_NAME_NOT_RESOLVED="Navnîşana IP ya rajekar nehatin dîtin"
ErrorCode.ERR_CONNECTION_TIMED_OUT="Dema girêdanê bi dawî bû"
ErrorCode.ERR_TIMED_OUT="Dema pêvajoyê qediya"
ErrorCode.ERR_FILE_NOT_FOUND="Pel nehate dîtin"
ErrorCode.ERR_FAILED="Girêdan pêk nehat"
ErrorCode.ERR_NETWORK_CHANGED="Tor guherî"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="Navrûya guhertoya SSL. Dibe ku TLS 1.3 were astengkirin an guhertin."
ErrorCode.ERR_SSL_PROTOCOL_ERROR="Çewtiya protokola SSL. Nikaribû girêdaneke ewledar çê bike."
ErrorCode.ERR_CERT_DATE_INVALID="Sertîfîkaya SSL ya rajekarê kevn e an jî dema kombersa te şaş e."
0707010000005A000081A400000000000000000000000168D5836B0000097A000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/ko-KR.iniLocalFile="로컬 파일"
Width="너비"
Height="높이"
CSS="사용자 지정 CSS"
ShutdownSourceNotVisible="소스가 보이지 않는 경우 종료"
RefreshBrowserActive="장면이 활성화되면 브라우저를 새로고침"
RefreshNoCache="현재 페이지의 캐시를 새로고침"
BrowserSource="브라우저"
CustomFrameRate="사용자가 임의로 설정한 프레임 레이트 사용"
RerouteAudio="OBS를 통해 오디오 조절"
Inspect="검사"
DevTools="브라우저 독 '%1' 검사"
CopyUrl="현재 주소 복사"
WebpageControlLevel="페이지 권한"
WebpageControlLevel.Level.None="OBS에 접근 권한 없음"
WebpageControlLevel.Level.ReadObs="OBS 상태 정보에 대한 읽기 권한"
WebpageControlLevel.Level.ReadUser="사용자 정보 (현재 장면 모음, 전환)에 대한 읽기 권한"
WebpageControlLevel.Level.Basic="OBS에 기본 접근 권한 (리플레이 버퍼 저장 등)"
WebpageControlLevel.Level.Advanced="OBS에 고급 접근 권한 (장면 전환, 리플레이 버퍼 시작/멈추기 등)"
WebpageControlLevel.Level.All="OBS에 완전 접근 권한 (경고문 없이 방송 시작/멈추기 등)"
Dialog.Alert="JavaScript 경고"
Dialog.Confirm="JavaScript 확인"
Dialog.Prompt="JavaScript 입력창"
Dialog.BrowserDock="브라우저 독"
Dialog.ReceivedFrom="'%1'에서 수신됨"
Error.Title="해당 페이지를 불러올 수 없습니다!"
Error.Description="주소가 올바른지, 또는 사이트에 이상이 없는지 확인하십시오"
Error.Retry="다시 시도하려면 여기를 클릭하십시오"
Error.Code="오류: %1"
Zoom.Reset="배율 초기화"
Zoom.Out="축소"
Zoom.In="확대"
ErrorCode.ERR_CONNECTION_REFUSED="서버가 연결을 거부했습니다"
ErrorCode.ERR_NAME_NOT_RESOLVED="서버의 IP주소를 찾을 수 없습니다"
ErrorCode.ERR_CONNECTION_TIMED_OUT="연결 시간이 초과되었습니다"
ErrorCode.ERR_TIMED_OUT="작업 시간 초과"
ErrorCode.ERR_FILE_NOT_FOUND="파일을 찾을 수 없음"
ErrorCode.ERR_FAILED="연결 실패"
ErrorCode.ERR_NETWORK_CHANGED="네트워크가 변경됨"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="SSL 버전 간섭. TLS1.3은 차단되거나 변조될 수 있습니다."
ErrorCode.ERR_SSL_PROTOCOL_ERROR="SSL 프로토콜 오류. 안전한 연결을 할 수 없습니다."
ErrorCode.ERR_CERT_DATE_INVALID="서버의 SSL 인증서가 오래되었거나 컴퓨터의 시간이 정확하지 않습니다."
0707010000005B000081A400000000000000000000000168D5836B00000739000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/lt-LT.iniLocalFile="Vietinis failas"
URL="Tinklalapio Adresas"
Width="Plotis"
Height="Aukštis"
FPS="Kadrai per sekundę"
CSS="Išplėstinis CSS"
ShutdownSourceNotVisible="Išjungti šaltinį, kai jis nėra matomas"
RefreshBrowserActive="Perkrauti naršyklę kai scena tampa aktyvi"
RefreshNoCache="Perkrauti dabartinio puslapio talpyklą"
BrowserSource="Naršyklė"
CustomFrameRate="Naudoti pasirinktiną kadrų dažnį"
RerouteAudio="Kontroliuoti garsą per OBS"
WebpageControlLevel="Puslapio leidimai"
WebpageControlLevel.Level.None="Nėra prieigos prie OBS"
WebpageControlLevel.Level.Basic="Paprasta prieiga prie OBS (Saugoti pakartojimo buferį ir panašiai)"
WebpageControlLevel.Level.Advanced="Pažengusi prieiga prie OBS (Pakeisti scenas, Pradėti/Stabdyti pakartojimo buferį ir panašiai)"
WebpageControlLevel.Level.All="Pilna prieiga prie OBS (Pradėti/Stabdyti transliavimą be įspėjimų ir panašiai)"
Error.Title="Nepavyko užkrauti to puslapio!"
Error.Description="Užtikrinkite, kad adresas yra tikslus, ir, kad svetainė šiuo metu neturi problemų."
Error.Retry="Spustelėkite čia, kad pabandyti iš naujo"
Error.Code="Klaida: %1"
Error.URL="Adresas: %2"
ErrorCode.ERR_CONNECTION_REFUSED="Serveris atisakė prisijungti"
ErrorCode.ERR_NAME_NOT_RESOLVED="Nerastas serverio IP adresas"
ErrorCode.ERR_CONNECTION_TIMED_OUT="Prijungimo laikas baigėsi"
ErrorCode.ERR_FILE_NOT_FOUND="Failas nerastas"
ErrorCode.ERR_FAILED="Nepavyko prisijungti"
ErrorCode.ERR_NETWORK_CHANGED="Pakeistas tinklas"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="SSL versijos trukdžiai. TLS 1.3 gali būti užblokuotas arba modifikuotas."
ErrorCode.ERR_SSL_PROTOCOL_ERROR="SSL protokolo klaida. Nepavyko užtikrinti saugaus prisijungimo."
ErrorCode.ERR_CERT_DATE_INVALID="Serverio SSL sertifikatas yra pasenęs arba jūsų kompiuterio laikas yra nustatytas neteisingai."
0707010000005C000081A400000000000000000000000168D5836B000001AD000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/mn-MN.iniLocalFile="Дотоод файл"
Width="Өргөн"
Height="Өндөр"
CSS="CSS тохиргоо"
ShutdownSourceNotVisible="Харагдахгүй байгаа үед эх үүсвэрийг унтраах"
RefreshBrowserActive="Хөшиг идэвхтэй болох үед хөтчийг дахин сэргээх"
RefreshNoCache="Байгай хуудасныхаа кэйшийг сэргээх"
BrowserSource="Хөтөч"
0707010000005D000081A400000000000000000000000168D5836B00000869000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/ms-MY.iniLocalFile="Fail setempat"
Width="Lebar"
Height="Tinggi"
CSS="CSS Suai"
ShutdownSourceNotVisible="Matikan sumber apabila tidak kelihatan"
RefreshBrowserActive="Segar semula pelayar ketika adegan menjadi aktif"
RefreshNoCache="Segar semula cache bagi halaman semasa"
BrowserSource="Pelayar"
CustomFrameRate="Guna kadar bingkai suai"
RerouteAudio="Kawal audio melalui OBS"
Inspect="Periksa"
DevTools="Periksa Labuh Pelayar '%1'"
CopyUrl="Salin adres semasa"
WebpageControlLevel="Keizinan halaman"
WebpageControlLevel.Level.None="Tiada capaian ke OBS"
WebpageControlLevel.Level.ReadObs="Capaian baca ke maklumat status OBS"
WebpageControlLevel.Level.ReadUser="Capaian baca ke maklumat pengguna (Koleksi, Peralihan Adegan semasa)"
WebpageControlLevel.Level.Basic="Capaian asas ke OBS (Simpan penimbal main semula, dll.)"
WebpageControlLevel.Level.Advanced="Capaian lanjutan ke OBS (Ubah adegan, Mula/Henti penimbal main semula, dll.)"
WebpageControlLevel.Level.All="Capaian penuh ke OBS (Mula/Henti penstriman tanpa amaran, dll.)"
Dialog.Alert="Amaran JavaScript"
Dialog.Confirm="Pengesahan JavaScript"
Dialog.Prompt="Makluman JavaScript"
Dialog.BrowserDock="Labuh Pelayar"
Dialog.ReceivedFrom="Diterima daripada '%1'"
Error.Title="Tidak dapat memuatkan halaman tersebut!"
Error.Description="Pastikan alamat adalah betul, dan tapak tiada hadapi masalah."
Error.Retry="Klik di sini untuk cuba lagi"
Error.Code="Ralat: %1"
Zoom.Reset="Set Semula Zoom"
Zoom.Out="Zoom keluar"
Zoom.In="Zoom masuk"
ErrorCode.ERR_CONNECTION_REFUSED="Pelayan menolak sambungan"
ErrorCode.ERR_NAME_NOT_RESOLVED="Alamat IP pelayan tidak ditemui"
ErrorCode.ERR_CONNECTION_TIMED_OUT="Sambungan tamat masa"
ErrorCode.ERR_TIMED_OUT="Sambungan tamat masa"
ErrorCode.ERR_FILE_NOT_FOUND="Fail tidak ditemui"
ErrorCode.ERR_FAILED="Sambungan gagal"
ErrorCode.ERR_NETWORK_CHANGED="Rangkaian berubah"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="Antara muka versi SSL. TLS 1.3 mungkin disekat atau diubah suai."
ErrorCode.ERR_SSL_PROTOCOL_ERROR="Ralat protokol SSL. Gagal menjalinkan sambungan selamat."
ErrorCode.ERR_CERT_DATE_INVALID="Sijil SSL pelayan sudah lapuk atau masa komputer anda salah."
0707010000005E000081A400000000000000000000000168D5836B00000687000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/nb-NO.iniLocalFile="Lokal fil"
Width="Bredde"
Height="Høyde"
CSS="Egendefinert CSS"
ShutdownSourceNotVisible="Deaktiver kilde når ikke synlig"
RefreshBrowserActive="Oppdater nettleseren når scenen blir aktiv"
RefreshNoCache="Oppdater bufferen for denne siden"
BrowserSource="Nettleser"
CustomFrameRate="Bruk tilpasset bildefrekvens"
RerouteAudio="Kontroller lyden gjennom OBS"
Inspect="Inspiser"
CopyUrl="Kopier nåværende adresse"
WebpageControlLevel="Side tillatelser"
WebpageControlLevel.Level.None="Ingen tilgang til OBS"
WebpageControlLevel.Level.Basic="Basistilgang til OBS (Lagre omspillingsbuffer, osv.)"
WebpageControlLevel.Level.Advanced="Avansert tilgang til OBS (endre sener, start/stopp omspillingsbuffer, osv.)"
WebpageControlLevel.Level.All="Full tilgang til OBS (Start/stopp kringkasting uten advarsel, osv.)"
Dialog.ReceivedFrom="Mottatt fra «%1»"
Error.Title="Kunne ikke laste inn den siden!"
Error.Description="Kontroller at adressen er riktig, og at nettstedet ikke har problemer."
Error.Retry="Klikk her for å prøve igjen."
Error.Code="Feil: %1"
Zoom.Reset="Tilbakestill forstørring"
Zoom.Out="Forminsk"
Zoom.In="Forstørr"
ErrorCode.ERR_CONNECTION_REFUSED="Serveren nektet tilkoblingen"
ErrorCode.ERR_NAME_NOT_RESOLVED="Serverens IP-adresse ble ikke funnet"
ErrorCode.ERR_CONNECTION_TIMED_OUT="Tilkoblingen ble tidsavbrutt"
ErrorCode.ERR_TIMED_OUT="Handlingen ble tidsavbrutt"
ErrorCode.ERR_FILE_NOT_FOUND="Fant ikke filen"
ErrorCode.ERR_FAILED="Tilkobling mislyktes"
ErrorCode.ERR_NETWORK_CHANGED="Nettverk er endret"
ErrorCode.ERR_CERT_DATE_INVALID="Enten så er tjenerens SSL-sertifikat er utdatert, eller så er datamaskinens klokkeslett galt."
0707010000005F000081A400000000000000000000000168D5836B00000884000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/nl-NL.iniLocalFile="Lokaal bestand"
Width="Breedte"
Height="Hoogte"
CSS="Aangepaste CSS"
ShutdownSourceNotVisible="Schakel bron uit wanneer deze niet zichtbaar is"
RefreshBrowserActive="Vernieuw browser wanneer een scène actief wordt"
RefreshNoCache="Cache van de huidige pagina vernieuwen"
CustomFrameRate="Gebruik aangepaste frame rate"
RerouteAudio="Bedien geluid via OBS"
Inspect="Inspecteren"
DevTools="Browser Dock '%1 ' inspecteren"
CopyUrl="Kopieer huidige adres"
WebpageControlLevel="Pagina rechten"
WebpageControlLevel.Level.None="Geen toegang tot OBS"
WebpageControlLevel.Level.ReadObs="Lees toegang tot OBS status informatie"
WebpageControlLevel.Level.ReadUser="Lees toegang tot gebruikersinformatie (huidige Scèneverzameling, Overgangen)"
WebpageControlLevel.Level.Basic="Basis toegang tot OBS (Opslaan replay buffer, etc.)"
WebpageControlLevel.Level.Advanced="Geavanceerde toegang tot OBS (Verander scènes, Start/Stop replay buffer, etc.)"
WebpageControlLevel.Level.All="Volledige toegang tot OBS (Start/Stop streamen zonder waarschuwing, etc.)"
Dialog.Alert="JavaScript waarschuwing"
Dialog.Confirm="JavaScript bevestiging"
Dialog.Prompt="JavaScript melding"
Dialog.ReceivedFrom="Ontvangen van '%1'"
Error.Title="Kon pagina niet laden!"
Error.Description="Controleer dat het adres correct is en dat de website geen problemen heeft."
Error.Retry="Klik hier om opnieuw te proberen"
Error.Code="Fout: %1"
Zoom.Reset="Reset zoom"
Zoom.Out="Zoom uit"
ErrorCode.ERR_CONNECTION_REFUSED="Server heeft de verbinding geweigerd"
ErrorCode.ERR_NAME_NOT_RESOLVED="Server's IP-adres niet gevonden"
ErrorCode.ERR_CONNECTION_TIMED_OUT="Connectie time-out"
ErrorCode.ERR_TIMED_OUT="Bewerking kreeg een time-out"
ErrorCode.ERR_FILE_NOT_FOUND="Bestand niet gevonden"
ErrorCode.ERR_FAILED="Kan geen verbinding maken"
ErrorCode.ERR_NETWORK_CHANGED="Netwerk gewijzigd"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="SSL versie interferentie. TLS 1.3 kan geblokkeerd of aangepast worden."
ErrorCode.ERR_SSL_PROTOCOL_ERROR="SSL-protocol fout. Kan geen beveiligde verbinding maken."
ErrorCode.ERR_CERT_DATE_INVALID="Het SSL-certificaat van de server is verouderd of de tijd van uw computer is onjuist."
07070100000060000081A400000000000000000000000168D5836B000000C4000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/nn-NO.iniLocalFile="Lokal fil"
Width="Breidde"
Height="Høgde"
BrowserSource="Nettlesar"
Error.Code="Feil: %1"
ErrorCode.ERR_FILE_NOT_FOUND="Fann ikkje fila"
ErrorCode.ERR_NETWORK_CHANGED="Nettverk endra"
07070100000061000081A400000000000000000000000168D5836B0000001A000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/oc-FR.iniBrowserSource="Navegador"
07070100000062000081A400000000000000000000000168D5836B0000095F000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/pl-PL.iniLocalFile="Plik lokalny"
URL="Adres URL"
Width="Szerokość"
Height="Wysokość"
FPS="FPS (kl./s)"
CSS="Własny CSS"
ShutdownSourceNotVisible="Zamknij źródło, gdy niewidoczne"
RefreshBrowserActive="Odśwież przeglądarkę, gdy scena zostanie aktywowana"
RefreshNoCache="Odśwież pamięć podręczną bieżącej strony"
BrowserSource="Przeglądarka"
CustomFrameRate="Własne ustawienia FPS (kl./s)"
RerouteAudio="Kontroluj dźwięk przez OBS"
Inspect="Zbadaj"
DevTools="Zbadaj panel przeglądarki '%1'"
CopyUrl="Skopiuj obecny adres"
WebpageControlLevel="Uprawnienia strony"
WebpageControlLevel.Level.None="Brak dostępu do OBS"
WebpageControlLevel.Level.ReadObs="Dostęp do odczytu informacji o statusie OBS"
WebpageControlLevel.Level.ReadUser="Dostęp do odczytu informacji o użytkowniku (aktualna kolekcja scen, przejścia)"
WebpageControlLevel.Level.Basic="Podstawowy dostęp do OBS (Zapisz bufor powtórki, etc.)"
WebpageControlLevel.Level.Advanced="Zaawansowany dostęp do OBS (Zmiana ekranów, Rozpoczęcie/Zatrzymanie bufora powtórki, etc.)"
WebpageControlLevel.Level.All="Pełny dostęp do OBS (Rozpocznij/Zakończ nadawanie bez ostrzeżenia, etc.)"
Dialog.Alert="Alert JavaScript"
Dialog.Confirm="Potwierdzenie JavaScript"
Dialog.Prompt="Zapytanie JavaScript"
Dialog.BrowserDock="Dock przeglądarki"
Dialog.ReceivedFrom="Wysłane przez '%1'"
Error.Title="Nie można załadować strony!"
Error.Description="Upewnij się, że adres jest poprawny i nie ma problemów ze stroną."
Error.Retry="Kliknij, aby spróbować ponownie"
Error.Code="Błąd: %1"
Zoom.Reset="Reset przybliżenia"
Zoom.Out="Oddal"
Zoom.In="Przybliż"
ErrorCode.ERR_CONNECTION_REFUSED="Serwer odmówił połączenia"
ErrorCode.ERR_NAME_NOT_RESOLVED="Nie znaleziono adresu IP serwera"
ErrorCode.ERR_CONNECTION_TIMED_OUT="Upłynął limit czasu połączenia"
ErrorCode.ERR_TIMED_OUT="Upłynął limit czasu tej operacji"
ErrorCode.ERR_FILE_NOT_FOUND="Nie znaleziono pliku"
ErrorCode.ERR_FAILED="Nie udało się połączyć"
ErrorCode.ERR_NETWORK_CHANGED="Konfiguracja sieci zmieniła się"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="Problemy z wersją SSL. TLS 1.3 może być zablokowany lub zmodyfikowany."
ErrorCode.ERR_SSL_PROTOCOL_ERROR="Błąd protokołu SSL. Nie można nawiązać bezpiecznego połączenia."
ErrorCode.ERR_CERT_DATE_INVALID="Nieaktualny certyfikat SSL serwera lub błędny czas Twojego komputera."
07070100000063000081A400000000000000000000000168D5836B0000092F000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/pt-BR.iniLocalFile="Arquivo local"
Width="Largura"
Height="Altura"
CSS="CSS personalizado"
ShutdownSourceNotVisible="Desativar fonte quando invisível"
RefreshBrowserActive="Atualizar o navegador quando a cena se tornar ativa"
RefreshNoCache="Atualizar o cache da página atual"
BrowserSource="Navegador"
CustomFrameRate="Usar taxa de quadros personalizada"
RerouteAudio="Controlar áudio via OBS"
Inspect="Inspecionar"
DevTools="Inspecionar painel personalizado '%1'"
CopyUrl="Copiar endereço atual"
WebpageControlLevel="Permissões da página"
WebpageControlLevel.Level.None="Sem acesso ao OBS"
WebpageControlLevel.Level.ReadObs="Acesso de leitura às informações de status do OBS"
WebpageControlLevel.Level.ReadUser="Acesso de leitura às informações do usuário (coleções de cenas, transições atuais)"
WebpageControlLevel.Level.Basic="Acesso básico ao OBS (salvar buffer de repetição, etc.)"
WebpageControlLevel.Level.Advanced="Acesso avançado ao OBS (mudar cenas, iniciar ou interromper buffer de repetição, etc.)"
WebpageControlLevel.Level.All="Acesso completo ao OBS (iniciar ou parar transmissão sem aviso, etc.)"
Dialog.Alert="Alerta de JavaScript"
Dialog.Confirm="Confirmação de JavaScript"
Dialog.Prompt="Prompt de JavaScript"
Dialog.BrowserDock="Painel de navegador"
Dialog.ReceivedFrom="Recebido de '%1'"
Error.Title="Não foi possível carregar essa página!"
Error.Description="Verifique se o endereço está correto e se o site não está com problemas."
Error.Retry="Tentar novamente"
Error.Code="Erro: %1"
Zoom.Reset="Redefinir zoom"
Zoom.Out="Diminuir zoom"
Zoom.In="Aumentar zoom"
ErrorCode.ERR_CONNECTION_REFUSED="Conexão recusada pelo servidor"
ErrorCode.ERR_NAME_NOT_RESOLVED="Endereço IP do servidor não encontrado"
ErrorCode.ERR_CONNECTION_TIMED_OUT="Conexão expirada"
ErrorCode.ERR_TIMED_OUT="Operação expirada"
ErrorCode.ERR_FILE_NOT_FOUND="Arquivo não encontrado"
ErrorCode.ERR_FAILED="Falha ao conectar"
ErrorCode.ERR_NETWORK_CHANGED="Rede alterada"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="Interferência na versão do SSL. TLS 1.3 pode estar bloqueado ou modificado."
ErrorCode.ERR_SSL_PROTOCOL_ERROR="Erro de protocolo SSL. Não foi possível fazer uma conexão segura."
ErrorCode.ERR_CERT_DATE_INVALID="O certificado SSL do servidor está desatualizado ou o relógio do seu computador está incorreto."
07070100000064000081A400000000000000000000000168D5836B00000918000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/pt-PT.iniLocalFile="Ficheiro local"
Width="Largura"
Height="Altura"
CSS="CSS personalizada"
ShutdownSourceNotVisible="Desligar fonte quando invisível"
RefreshBrowserActive="Atualizar o navegador quando a cena se ativar"
RefreshNoCache="Atualizar memória da página atual"
BrowserSource="Navegador"
CustomFrameRate="Usar taxa de fotogramas personalizada"
RerouteAudio="Controlar áudio via OBS"
Inspect="Inspecionar"
DevTools="Inspecionar doca do navegador '%1'"
CopyUrl="Copiar endereço atual"
WebpageControlLevel="Permissões da página"
WebpageControlLevel.Level.None="Sem acesso ao OBS"
WebpageControlLevel.Level.ReadObs="Acesso de leitura às informações de estado do OBS"
WebpageControlLevel.Level.ReadUser="Acesso de leitura às informações do utilizador (coleção de cenas atual, transições)"
WebpageControlLevel.Level.Basic="Acesso básico ao OBS (guardar memória de repetição, etc.)"
WebpageControlLevel.Level.Advanced="Acesso avançado ao OBS (mudar cenas, iniciar/parar memória de reprodução, etc.)"
WebpageControlLevel.Level.All="Acesso completo ao OBS (iniciar/parar transmissão sem aviso, etc.)"
Dialog.Alert="Alerta de JavaScript"
Dialog.Confirm="Confirmação do JavaScript"
Dialog.Prompt="Diálogo JavaScript"
Dialog.BrowserDock="Doca de navegador"
Dialog.ReceivedFrom="Recebido de '%1'"
Error.Title="Impossível carregar a página!"
Error.Description="Verifique se o endereço está correto e se a página não tem problemas."
Error.Retry="Clique aqui para tentar novamente"
Error.Code="Erro: %1"
Zoom.Reset="Repor zoom"
Zoom.Out="Diminuir zoom"
Zoom.In="Aumentar zoom"
ErrorCode.ERR_CONNECTION_REFUSED="O servidor recusou a ligação"
ErrorCode.ERR_NAME_NOT_RESOLVED="Endereço IP do servidor não encontrado"
ErrorCode.ERR_CONNECTION_TIMED_OUT="A ligação expirou"
ErrorCode.ERR_TIMED_OUT="Operação expirada"
ErrorCode.ERR_FILE_NOT_FOUND="Ficheiro não encontrado"
ErrorCode.ERR_FAILED="Falha ao ligar"
ErrorCode.ERR_NETWORK_CHANGED="Rede alterada"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="interferência na versão SSL. TLS 1.3 pode estar bloqueado ou modificado."
ErrorCode.ERR_SSL_PROTOCOL_ERROR="Erro de protocolo SSL. Impossível fazer uma ligação segura."
ErrorCode.ERR_CERT_DATE_INVALID="O certificado SSL do servidor está desatualizado ou a data/hora do seu computador está incorreta."
07070100000065000081A400000000000000000000000168D5836B0000093F000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/ro-RO.iniLocalFile="Fișier local"
Width="Lățime"
Height="Înălțime"
CSS="CSS personalizat"
ShutdownSourceNotVisible="Închide sursa când nu este vizibilă"
RefreshBrowserActive="Reîmprospătează browserul când scena devine activă"
RefreshNoCache="Reîmprospătează memoria cache a paginii actuale"
CustomFrameRate="Folosește o frecvență de cadre personalizată"
RerouteAudio="Controlează sistemul audio prin OBS"
Inspect="Inspectează"
DevTools="Inspectează andocarea de browser '%1'"
CopyUrl="Copiază adresa actuală"
WebpageControlLevel="Permisiunile paginii"
WebpageControlLevel.Level.None="Fără acces la OBS"
WebpageControlLevel.Level.ReadObs="Acces de citire a informațiilor de stare OBS"
WebpageControlLevel.Level.ReadUser="Acces de citire a informațiilor utilizatorului (colecția de scene actuală, tranziții)"
WebpageControlLevel.Level.Basic="Acces de bază la OBS (salvarea bufferului de reluări etc.)"
WebpageControlLevel.Level.Advanced="Acces avansat la OBS (schimbarea scenelor, pornirea/oprirea bufferului de reluări etc.)"
WebpageControlLevel.Level.All="Acces complet la OBS (pornirea/oprirea streamingului fără avertisment etc.)"
Dialog.Alert="Alertă JavaScript"
Dialog.Confirm="Confirmare JavaScript"
Dialog.Prompt="Prompt JavaScript"
Dialog.BrowserDock="Andocare de browser"
Dialog.ReceivedFrom="Primit de la '%1'"
Error.Title="Nu s-a putut încărca acea pagină!"
Error.Description="Asigurați-vă că adresa este corectă și că site-ul nu are probleme."
Error.Retry="Clic aici pentru a reîncerca"
Error.Code="Eroare: %1"
Zoom.Reset="Resetează mărire"
Zoom.Out="Micșorare"
Zoom.In="Mărire"
ErrorCode.ERR_CONNECTION_REFUSED="Serverul a refuzat conexiunea"
ErrorCode.ERR_NAME_NOT_RESOLVED="Adresa IP a serverului nu a fost găsită"
ErrorCode.ERR_CONNECTION_TIMED_OUT="Conexiunea a expirat"
ErrorCode.ERR_TIMED_OUT="Operațiunea a expirat"
ErrorCode.ERR_FILE_NOT_FOUND="Fișier negăsit"
ErrorCode.ERR_FAILED="Conectare nereușită"
ErrorCode.ERR_NETWORK_CHANGED="Rețea schimbată"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="Interferența versiunii SSL. TLS 1.3 poate fi blocată sau modificată."
ErrorCode.ERR_SSL_PROTOCOL_ERROR="Eroare de protocol SSL. Nu s-a putut realiza o conexiune securizată."
ErrorCode.ERR_CERT_DATE_INVALID="Certificatul SSL al serverului este învechit sau ora computerului dvs. este incorectă."
07070100000066000081A400000000000000000000000168D5836B00000D53000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/ru-RU.iniLocalFile="Локальный файл"
URL="Ссылка"
Width="Ширина"
Height="Высота"
CSS="CSS"
ShutdownSourceNotVisible="Отключать источник, когда не видим"
RefreshBrowserActive="Обновить браузер, когда сцена становится активной"
RefreshNoCache="Обновить кэш текущей страницы"
BrowserSource="Браузер"
CustomFrameRate="Использовать настраиваемую частоту кадров"
RerouteAudio="Управление аудио через OBS"
Inspect="Осмотреть"
DevTools="Осмотреть док-панель браузера «%1»"
CopyUrl="Скопировать текущий адрес"
WebpageControlLevel="Разрешение страницы"
WebpageControlLevel.Level.None="Нет доступа к OBS"
WebpageControlLevel.Level.ReadObs="Доступ чтения информации о состоянии OBS"
WebpageControlLevel.Level.ReadUser="Доступ чтения пользовательской информации (текущая коллекция сцен, переходы)"
WebpageControlLevel.Level.Basic="Базовый доступ к OBS (Сохранение буфер видео и т.д.)"
WebpageControlLevel.Level.Advanced="Продвинутый доступ к OBS (Изменение сцен, запуск/остановка буфера записи и пр.)"
WebpageControlLevel.Level.All="Полный доступ к OBS (Запуск/Остановка прямого эфира без предупреждения и пр.)"
Dialog.Alert="Предупреждение JavaScript"
Dialog.Confirm="Подтверждение JavaScript"
Dialog.Prompt="Подсказка JavaScript"
Dialog.BrowserDock="Док-панель браузера"
Dialog.ReceivedFrom="Получено от «%1»"
Error.Title="Не удалось загрузить эту страницу!"
Error.Description="Проверьте правильность адреса и что сайт доступен."
Error.Retry="Нажмите здесь, чтобы повторить попытку"
Error.Code="Ошибка: %1"
Error.URL="Ссылка: %2"
Zoom.Reset="Сбросить масштаб"
Zoom.Out="Уменьшить масштаб"
Zoom.In="Увеличить масштаб"
ErrorCode.ERR_CONNECTION_REFUSED="Сервер отклонил подключение"
ErrorCode.ERR_NAME_NOT_RESOLVED="IP-адрес сервера не найден"
ErrorCode.ERR_CONNECTION_TIMED_OUT="Время ожидания соединения истекло"
ErrorCode.ERR_TIMED_OUT="Время ожидания операции истекло"
ErrorCode.ERR_FILE_NOT_FOUND="Файл не найден"
ErrorCode.ERR_FAILED="Не удалось подключиться"
ErrorCode.ERR_NETWORK_CHANGED="Сеть изменена"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="Вмешательство в версию SSL. Возможно, TLS 1.3 заблокирован или модифицирован."
ErrorCode.ERR_SSL_PROTOCOL_ERROR="Ошибка протокола SSL. Не удалось установить защищённое соединение."
ErrorCode.ERR_CERT_DATE_INVALID="SSL-сертификат сервера устарел, или на вашем компьютере неверно указано время."
07070100000067000081A400000000000000000000000168D5836B00000970000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/si-LK.iniLocalFile="ස්ථානීය ගොනුව"
URL="ඒ.ස.නි."
Width="පළල"
Height="උස"
FPS="රාමු/ත."
CSS="අභිරුචි සීඑස්එස්"
ShutdownSourceNotVisible="නොපෙනෙන විට මූලාශ්රය වසන්න"
RefreshBrowserActive="සිද්ධිය සක්රිය වූ විට අතිරික්සුව නැවුම් කරන්න"
RefreshNoCache="වත්මන් පිටුවේ නිහිතය නැවුම් කරන්න"
BrowserSource="අතිරික්සුව"
CustomFrameRate="අභිරුචි රාමු අනුපා. භාවිතය"
RerouteAudio="ඕබීඑස් මගින් හඬ පාලනය"
CopyUrl="වත්මන් ලිපිනයේ පිටපතක්"
WebpageControlLevel="පිටුවේ අවසර"
WebpageControlLevel.Level.None="ඕබීඑස් වෙත ප්රවේශය නැත"
WebpageControlLevel.Level.ReadObs="ඕබීඑස් තත්ව තොරතුරු වෙත කියවීමේ ප්රවේශය"
Dialog.Alert="ජාවාස්ක්රිප්ට් ඇඟවීම"
Dialog.Confirm="ජාවාස්ක්රිප්ට් තහවුරුව"
Dialog.BrowserDock="අතිරික්සු තටාකය"
Dialog.ReceivedFrom="'%1' වෙතින් ලැබිණි"
Error.Title="පිටුව පූරණය කිරීමට නොහැකි විය!"
Error.Retry="යළි උත්සාහයට ඔබන්න"
Error.Code="දෝෂය: %1"
Error.URL="ඒ.ස.නි.: %2"
Zoom.Reset="විශාලනය යළි සකසන්න"
Zoom.Out="කුඩාලනය"
Zoom.In="විශාලනය"
ErrorCode.ERR_CONNECTION_REFUSED="සේවාදායකය සම්බන්ධතාවය අහක දමා ඇත"
ErrorCode.ERR_NAME_NOT_RESOLVED="සේවාදායකයේ අ.ජා.කෙ. හමු නොවිණි"
ErrorCode.ERR_CONNECTION_TIMED_OUT="සම්බන්ධතාවය ඉකුත් විය"
ErrorCode.ERR_TIMED_OUT="මෙහෙයුම ඉකුත් විය"
ErrorCode.ERR_FILE_NOT_FOUND="ගොනුව හමු නොවිණි"
ErrorCode.ERR_FAILED="සම්බන්ධ වීමට අසමත් විය"
ErrorCode.ERR_NETWORK_CHANGED="ජාලය වෙනස් විණි"
07070100000068000081A400000000000000000000000168D5836B00000932000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/sk-SK.iniLocalFile="Lokálny súbor"
Width="Šírka"
Height="Výška"
CSS="Vlastné CSS"
ShutdownSourceNotVisible="Vypnúť zdroj, keď nie je viditeľný"
RefreshBrowserActive="Obnoviť prehliadač, keď sa scéna stane aktívna"
RefreshNoCache="Vyčistiť cache aktuálnej stránky"
BrowserSource="Prehliadač"
CustomFrameRate="Použiť vlastnú snímkovú rýchlosť"
RerouteAudio="Ovládať zvuk cez OBS"
Inspect="Preskúmať"
DevTools="Preskúmať dok prehliadača '%1'"
CopyUrl="Kopírovať aktuálnu adresu"
WebpageControlLevel="Oprávnenia stránky"
WebpageControlLevel.Level.None="Žiaden prístup k OBS"
WebpageControlLevel.Level.ReadObs="Povolenie čítať informácie o stave OBS"
WebpageControlLevel.Level.ReadUser="Povolenie čítať použivateľské informácie (aktuálna sada scén, prechody)"
WebpageControlLevel.Level.Basic="Základný prístup k OBS (Uložiť záznam do pamäte, atď.)"
WebpageControlLevel.Level.Advanced="Pokročilý prístup k OBS (Meniť scény, Spustiť/Zastaviť záznam do pamäte, atď.)"
WebpageControlLevel.Level.All="Plný prístup k OBS (Spustiť/Zastaviť streamovanie bez upozornenia, atď.)"
Dialog.Alert="JavaScript Výstraha"
Dialog.Confirm="JavaScript Potvrdenie"
Dialog.Prompt="Javascript Výzva"
Dialog.BrowserDock="Dok prehliadača"
Dialog.ReceivedFrom="Prijaté z '%1'"
Error.Title="Nepodarilo sa načítať túto stránku!"
Error.Description="Uistite sa, že adresa je správna a že stránka nemá problémy."
Error.Retry="Ak to chcete skúsiť znova, kliknite sem"
Error.Code="Chyba: %1"
Zoom.Reset="Obnoviť priblíženie"
Zoom.Out="Oddialiť"
Zoom.In="Priblížiť"
ErrorCode.ERR_CONNECTION_REFUSED="Server odmietol pripojenie"
ErrorCode.ERR_NAME_NOT_RESOLVED="IP adresa servera nenájdená"
ErrorCode.ERR_CONNECTION_TIMED_OUT="Vypršal časový limit pripojenia"
ErrorCode.ERR_TIMED_OUT="Čas operácie vypršal"
ErrorCode.ERR_FILE_NOT_FOUND="Súbor nenájdený"
ErrorCode.ERR_FAILED="Nepodarilo sa pripojiť"
ErrorCode.ERR_NETWORK_CHANGED="Sieť zmenená"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="Problém s SSL verziou. TLS 1.3 môže byť zablokovaná alebo upravená."
ErrorCode.ERR_SSL_PROTOCOL_ERROR="Chyba SSL protokolu. Nebolo možné vytvoriť zabezpečené pripojenie."
ErrorCode.ERR_CERT_DATE_INVALID="SSL certifikát servera je neplatný alebo je čas na vašom počítači nesprávny."
07070100000069000081A400000000000000000000000168D5836B0000091F000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/sl-SI.iniLocalFile="Lokalna datoteka"
Width="Širina"
Height="Višina"
FPS="sl./s"
CSS="CSS po meri"
ShutdownSourceNotVisible="Izklopi vir, ko ni viden"
RefreshBrowserActive="Osveži brskalnik, ko prizor postane dejaven"
RefreshNoCache="Osveži medpomnilnik trenutne strani"
BrowserSource="Brskalnik"
CustomFrameRate="Uporabi hitrost sličic po meri"
RerouteAudio="Nadziraj zvok preko OBS-a"
Inspect="Preuči"
DevTools="Preuči sidrišče brskalnika %1"
CopyUrl="Kopiraj trenutni naslov"
WebpageControlLevel="Dovoljenja strani"
WebpageControlLevel.Level.None="Ni dostopa do OBS"
WebpageControlLevel.Level.ReadObs="Dostop za branje podatkov o stanju OBS"
WebpageControlLevel.Level.ReadUser="Dostop za branje uporabniških podatkov (trenutno zbir prizorov, prehodi)"
WebpageControlLevel.Level.Basic="Osnovni dostop do OBS (snemanje medpomnilnika za ponovitve, itd.)"
WebpageControlLevel.Level.Advanced="Napredni dostop do OBS (spreminjanje scen, zagon/zaustavitev medpomnilnika s ponovitvami, itd.)"
WebpageControlLevel.Level.All="Poln dostop do OBS (zagon in zaustavitev pretkoa brez opozoril, itd.)"
Dialog.Alert="Opozorilo JavaScript"
Dialog.Confirm="Potrdi JavaScript"
Dialog.Prompt="Vprašanje JavaScript"
Dialog.BrowserDock="Sidrišče brskalnika"
Dialog.ReceivedFrom="Prejeto iz %1"
Error.Title="Strani ni bilo mogoče naložiti!"
Error.Description="Preverite pravilnost naslova strani in da stran nima težav."
Error.Retry="Kliknite sem za ponoven poskus"
Error.Code="Napaka: %1"
Error.URL="URL naslov: %2"
Zoom.Reset="Ponastavi povečavo"
Zoom.Out="Pomanjšaj"
Zoom.In="Povečaj"
ErrorCode.ERR_CONNECTION_REFUSED="Strežnik je zavrnil povezavo"
ErrorCode.ERR_NAME_NOT_RESOLVED="IP naslov strežnika ni bil najden"
ErrorCode.ERR_CONNECTION_TIMED_OUT="Čas za povezovanje se je iztekel"
ErrorCode.ERR_TIMED_OUT="Opravilo je poteklo"
ErrorCode.ERR_FILE_NOT_FOUND="Datoteke ni mogoče najti"
ErrorCode.ERR_FAILED="Vzpostavljanje povezave ni uspelo"
ErrorCode.ERR_NETWORK_CHANGED="Omrežje je bilo zamenjano"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="Interferenca v SSL različici. TLS 1.3 je lahko blokiran ali spremenjen."
ErrorCode.ERR_SSL_PROTOCOL_ERROR="Napaka v SSL protokolu. Varne povezave ni bilo mogoče vzpostaviti."
ErrorCode.ERR_CERT_DATE_INVALID="Potrdilo SSL strežnika je preteklo ali pa je čas vašega računalnika napačen."
0707010000006A000081A400000000000000000000000168D5836B00000041000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/sq-AL.iniWidth="Gjerësia"
Height="Lartësia"
CSS="CSS e personalizuar:"
0707010000006B000081A400000000000000000000000168D5836B0000016F000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/sr-CS.iniLocalFile="Lokalni fajl"
URL="URL Adresa"
Width="Širina"
Height="Visina"
CSS="Prilagođeni CSS"
ShutdownSourceNotVisible="Isključi izvor kada nije vidljiv"
RefreshBrowserActive="Osveži pretraživač kada scena postane aktivna"
RefreshNoCache="Osveži keš memoriju trenutne stranice"
BrowserSource="Pretraživač"
CustomFrameRate="Koristi podešenu brzinu frejma"
0707010000006C000081A400000000000000000000000168D5836B00000D3F000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/sr-SP.iniLocalFile="Локални фајл"
URL="URL адреса"
Width="Ширина"
Height="Висина"
CSS="Прилагођени CSS"
ShutdownSourceNotVisible="Искључи извор када није видљив"
RefreshBrowserActive="Освежи претраживач када сцена постане активна"
RefreshNoCache="Освежи кеш меморију тренутне странице"
BrowserSource="Претраживач"
CustomFrameRate="Користи подешену брзину фрејма"
RerouteAudio="Управљање звуком преко OBS-а"
Inspect="Прегледајте"
DevTools="Проверите док прегледача „%1“"
CopyUrl="Копирај тренутну адресу"
WebpageControlLevel="Дозволе за страницу"
WebpageControlLevel.Level.None="Нема приступа OBS-у"
WebpageControlLevel.Level.ReadObs="Приступ за читање информација о статусу OBS-а"
WebpageControlLevel.Level.ReadUser="Приступ за читање корисничких информација (тренутна колекција сцена, прелази)"
WebpageControlLevel.Level.Basic="Основни приступ OBS-у (сачувај бафер за репродукцију, итд.)"
WebpageControlLevel.Level.Advanced="Напредни приступ OBS-у (промена сцена, покретање/заустављање меморије за репродукцију итд.)"
WebpageControlLevel.Level.All="Потпун приступ OBS-у (Покретање/Заустављање стримовања без упозорења, итд.)"
Dialog.Alert="Упозорење о ЈаваСкрипту"
Dialog.Confirm="Потврда ЈаваСкрипта"
Dialog.Prompt="Јаваскрипт промпт"
Dialog.BrowserDock="Док прегледача"
Dialog.ReceivedFrom="Примљено од '%1'"
Error.Title="Није могуће учитати ту страницу!"
Error.Description="Уверите се да је адреса тачна и да сајт нема проблема."
Error.Retry="Кликните овде да покушате поново"
Error.Code="Грешка: %1"
Zoom.Reset="Ресетуј зумирање"
Zoom.Out="Умањи"
Zoom.In="Увећај"
ErrorCode.ERR_CONNECTION_REFUSED="Сервер је одбио везу"
ErrorCode.ERR_NAME_NOT_RESOLVED="IP адреса сервера није пронађена"
ErrorCode.ERR_CONNECTION_TIMED_OUT="Временско ограничење везе је истекло"
ErrorCode.ERR_TIMED_OUT="Временско ограничење операције је истекло"
ErrorCode.ERR_FILE_NOT_FOUND="Датотека није пронађена"
ErrorCode.ERR_FAILED="Повезивање није успело"
ErrorCode.ERR_NETWORK_CHANGED="Мрежа је промењена"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="Интерференција SSL верзије. TLS 1.3 је можда блокиран или модификован."
ErrorCode.ERR_SSL_PROTOCOL_ERROR="Грешка SSL протокола. Није могуће успоставити безбедну везу."
ErrorCode.ERR_CERT_DATE_INVALID="SSL сертификат сервера је застарео или је време на вашем рачунару нетачно."
0707010000006D000081A400000000000000000000000168D5836B00000902000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/sv-SE.iniLocalFile="Lokal fil"
Width="Bredd"
Height="Höjd"
FPS="Bildfrekvens"
CSS="Anpassad CSS"
ShutdownSourceNotVisible="Stäng ned källor som inte visas"
RefreshBrowserActive="Uppdatera webbläsaren när scenen blir aktiv"
RefreshNoCache="Uppdatera cachen för aktuell sida"
BrowserSource="Webbläsare"
CustomFrameRate="Använd anpassad bildfrekvens"
RerouteAudio="Kontrollera ljudet via OBS"
Inspect="Inspektera"
DevTools="Inspektera webbläsarfönstret \"%1\""
CopyUrl="Kopiera den nuvarande adressen"
WebpageControlLevel="Sidbehörigheter"
WebpageControlLevel.Level.None="Ingen åtkomst till OBS"
WebpageControlLevel.Level.ReadObs="Läsåtkomst till OBS:s statusinformation"
WebpageControlLevel.Level.ReadUser="Läsåtkomst till användarinformation (nuvarande scensamling, övergångar)"
WebpageControlLevel.Level.Basic="Grundläggande åtkomst till OBS (spara reprisbuffert, etc.)"
WebpageControlLevel.Level.Advanced="Avancerad åtkomst till OBS (ändra scener, starta/stoppa reprisbuffert, osv.)"
WebpageControlLevel.Level.All="Full åtkomst till OBS (starta/stoppa strömmar utan förvarning, etc.)"
Dialog.Alert="JavaScript-meddelande"
Dialog.Confirm="JavaScript-bekräftelse"
Dialog.Prompt="JavaScript-fråga"
Dialog.BrowserDock="Flytande webbläsarfönster"
Dialog.ReceivedFrom="Hämtades från \"%1\""
Error.Title="Kunde inte ladda sidan!"
Error.Description="Se till att adressen är korrekt, och att webbplatsen inte har problem."
Error.Retry="Klicka här för att försöka igen"
Error.Code="Fel: %1"
Zoom.Reset="Återställ zoom"
Zoom.Out="Zooma ut"
Zoom.In="Zooma in"
ErrorCode.ERR_CONNECTION_REFUSED="Servern nekade anslutningen"
ErrorCode.ERR_NAME_NOT_RESOLVED="Serverns IP-adress hittades inte"
ErrorCode.ERR_CONNECTION_TIMED_OUT="Anslutningen tog för lång tid"
ErrorCode.ERR_TIMED_OUT="Åtgärdens tidsgräns gick ut"
ErrorCode.ERR_FILE_NOT_FOUND="Filen hittades inte"
ErrorCode.ERR_FAILED="Det gick inte att ansluta"
ErrorCode.ERR_NETWORK_CHANGED="Nätverket ändrades"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="Versionskonflikt för SSL. TLS 1.3 kanske har blockerats eller ändrats."
ErrorCode.ERR_SSL_PROTOCOL_ERROR="Protokollfel för SSL. Kunde inte skapa en säker anslutning."
ErrorCode.ERR_CERT_DATE_INVALID="Serverns SSL-certifikat är föråldrat eller din datortid är felaktig."
0707010000006E000081A400000000000000000000000168D5836B000003C6000000000000000000000000000000000000004C00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/szl-PL.iniLocalFile="Zbiōr lokalny"
Width="Szyrokość"
Height="Wysokość"
CSS="Włosny CSS"
ShutdownSourceNotVisible="Zawrzij zdrzōdło, kej je niywidzialne"
RefreshBrowserActive="Ôdświyż przeziyroczka, kej bina je aktywowano"
RefreshNoCache="Ôdświyż podrynczno spamiyńć teroźnyj zajty"
BrowserSource="Przeziyroczka"
CustomFrameRate="Włosne ôpcyje FPS"
RerouteAudio="Kōntroluj audio bez OBSa"
Error.Title="Niy idzie zaladować zajty!"
Error.Description="Dej pozōr, eli adresa je dobro, iże zajta tyż funguje dobrze."
Error.Retry="Kliknij, coby sprōbować zaś"
Error.Code="Feler: %1"
ErrorCode.ERR_CONNECTION_REFUSED="Serwer ôdkozoł połōnczynio"
ErrorCode.ERR_NAME_NOT_RESOLVED="Niy idzie znojść adresy IP ôd serwera"
ErrorCode.ERR_CONNECTION_TIMED_OUT="Upłynōł limit czasu połōnczynio"
ErrorCode.ERR_FILE_NOT_FOUND="Niy idzie znojść zbioru"
ErrorCode.ERR_FAILED="Niy idzie sie połōnczyć"
ErrorCode.ERR_NETWORK_CHANGED="Nec je zmiyniōny"
0707010000006F000081A400000000000000000000000168D5836B000003F8000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/ta-IN.iniLocalFile="உள்நோக்குக் கோப்பு"
Width="அகலம்"
Height="உயரம்"
CSS="தனிப்பயன் CSS"
ShutdownSourceNotVisible="காண்பிக்கபடாதபோது நிறுத்தி வைக்க"
RefreshBrowserActive="காண்பிக்கும்போது போது உலாவியைப் புதுப்பிக்கவும்"
RefreshNoCache="தற்போதைய பக்கத்தின் தற்காலிக சேமிப்பை புதுப்பிக்கவும்"
BrowserSource="உலாவி"
CustomFrameRate="தனிப்பயன் பிரேம் வீதத்தைப் பயன்படுத்தவும்"
RerouteAudio="OBS மூலம் ஒலியைக் கட்டுப்படுத்து"
WebpageControlLevel="பக்க அனுமதிகள்"
WebpageControlLevel.Level.None="OBS க்கு அணுகல் இல்லை"
07070100000070000081A400000000000000000000000168D5836B00001025000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/th-TH.iniLocalFile="ไฟล์ในเครื่อง"
Width="ความกว้าง"
Height="ความสูง"
FPS="อัตราเฟรม"
CSS="CSS ที่กำหนดเอง"
ShutdownSourceNotVisible="วิดีโอจะหยุดทำงานถ้าคุณซ่อนแหล่ง"
RefreshBrowserActive="วิดีโอที่จะทำงานต่อไปแม้จะไม่ปรากฏบนหน้าจอ"
RefreshNoCache="รีเฟรชแคชของหน้าปัจจุบัน"
BrowserSource="เว็บเบราว์เซอร์"
CustomFrameRate="ใช้อัตราเฟรมที่กำหนดเอง"
RerouteAudio="ควบคุมเสียงผ่าน OBS"
Inspect="ตรวจสอบ"
DevTools="ตรวจสอบ Dock ของเบราว์เซอร์ '%1'"
CopyUrl="คัดลอกที่อยู่ปัจจุบัน"
WebpageControlLevel="การอนุญาตเพจ"
WebpageControlLevel.Level.None="ไม่มีการเข้าถึง OBS"
WebpageControlLevel.Level.ReadObs="อ่านการเข้าถึงข้อมูลสถานะ OBS"
WebpageControlLevel.Level.ReadUser="สิทธิ์การอ่านข้อมูลถึงผู้ใช้ (การรวบรวมฉากปัจจุบัน รวมการเปลี่ยนฉาก)"
WebpageControlLevel.Level.Basic="การเข้าถึง OBS ขั้นพื้นฐาน (บันทึก บัฟเฟอร์ การเล่นซ้ำ ฯลฯ )"
WebpageControlLevel.Level.Advanced="การเข้าถึง OBS ขั้นสูง (เปลี่ยนฉาก บัฟเฟอร์ เริ่ม/หยุด เล่นซ้ำ ฯลฯ)"
WebpageControlLevel.Level.All="เข้าถึง OBS อย่างเต็มรูปแบบ (เริ่ม/หยุด การสตรีม โดยไม่มีการแจ้งเตือน ฯลฯ)"
Dialog.Alert="การแจ้งเตือน JavaScript"
Dialog.Confirm="JavaScript ยืนยัน"
Dialog.Prompt="พรอมต์ JavaScript"
Dialog.BrowserDock="เบราเซอร์ด็อก"
Dialog.ReceivedFrom="ที่ได้รับจาก '%1'"
Error.Title="ไม่สามารถโหลดหน้านี้ได้!"
Error.Description="ตรวจสอบให้แน่ใจว่าที่อยู่ถูกต้อง และเว็บไซต์ไม่มีปัญหา"
Error.Retry="คลิกเพื่อลองอีกครั้ง"
Error.Code="ผิดพลาด: %1"
Zoom.Reset="กลับค่าเดิมของการซูม"
Zoom.Out="ซูมออก"
Zoom.In="ซูมเข้า"
ErrorCode.ERR_CONNECTION_REFUSED="เซิร์ฟเวอร์ปฏิเสธการเชื่อมต่อ"
ErrorCode.ERR_NAME_NOT_RESOLVED="ไม่พบที่อยู่ IP ของเซิร์ฟเวอร์"
ErrorCode.ERR_CONNECTION_TIMED_OUT="หมดเวลาการเชื่อมต่อ"
ErrorCode.ERR_TIMED_OUT="การดำเนินการหมดเวลา"
ErrorCode.ERR_FILE_NOT_FOUND="ไม่พบไฟล์"
ErrorCode.ERR_FAILED="การเชื่อมต่อล้มเหลว"
ErrorCode.ERR_NETWORK_CHANGED="เปลี่ยนเครือข่าย"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="การรบกวนเวอร์ชัน SSL TLS 1.3 อาจถูกบล็อกหรือแก้ไข"
ErrorCode.ERR_SSL_PROTOCOL_ERROR="ข้อผิดพลาดของโปรโตคอล SSL ไม่สามารถทำการเชื่อมต่อที่ปลอดภัย"
ErrorCode.ERR_CERT_DATE_INVALID="ใบรับรอง SSL ของเซิร์ฟเวอร์ล้าสมัยหรือเวลาของคอมพิวเตอร์นั้นไม่ถูกต้อง"
07070100000071000081A400000000000000000000000168D5836B00000939000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/tr-TR.iniLocalFile="Yerel dosya"
URL="Bağlantı Adresi"
Width="Genişlik"
Height="Yükseklik"
CSS="Özel CSS"
ShutdownSourceNotVisible="Görünür değilken kaynağı kapat"
RefreshBrowserActive="Sahne etkin olduğunda tarayıcıyı yenile"
RefreshNoCache="Geçerli sayfanın önbelleğini yenile"
BrowserSource="Tarayıcı"
CustomFrameRate="Özel kare hızı kullan"
RerouteAudio="OBS ile sesi kontrol etme"
Inspect="Denetle"
DevTools="'%1' Tarayıcı Yuvasını Denetle"
CopyUrl="Geçerli adresi kopyala"
WebpageControlLevel="Sayfa İzinleri"
WebpageControlLevel.Level.None="OBS'ye erişim yok"
WebpageControlLevel.Level.ReadObs="OBS'ye erişme durumu bilgilerini okuma"
WebpageControlLevel.Level.ReadUser="Kullanıcıya erişme bilgilerini okuma (anlık Sahne Koleksiyonu, Geçişler)"
WebpageControlLevel.Level.Basic="OBS'ye basit erişim (Tekrar oynatma arabelleği kaybetme, vb.)"
WebpageControlLevel.Level.Advanced="OBS'ye gelişmiş erişim (Sahne değiştirme, Tekrar oynatma arabelleğini durdurma/başlatma, vb.)"
WebpageControlLevel.Level.All="OBS'ye tam erişim (Uyarı vermeden yayın başlatma/durdurma, vb.)"
Dialog.Alert="JavaScript Uyarısı"
Dialog.Confirm="JavaScript Onayla"
Dialog.Prompt="JavaScript İstemi"
Dialog.BrowserDock="Tarayıcı Dock"
Dialog.ReceivedFrom="Alındığı yer '%1'"
Error.Title="Sayfa yüklenemedi!"
Error.Description="Adresin doğruluğunu ve sitenin sorunları olmadığından emin olun."
Error.Retry="Yeniden denemek için buraya tıklayın"
Error.Code="Hata: %1"
Error.URL="Bağlantı Adresi: %2"
Zoom.Reset="Yakınlaştırmayı sıfırla"
Zoom.Out="Uzaklaştır"
Zoom.In="Yakınlaştır"
ErrorCode.ERR_CONNECTION_REFUSED="Sunucu bağlantıyı reddetti."
ErrorCode.ERR_NAME_NOT_RESOLVED="Sunucunun IP adresi bulunamadı"
ErrorCode.ERR_CONNECTION_TIMED_OUT="Bağlantı zaman aşımına uğradı"
ErrorCode.ERR_TIMED_OUT="İşlem zaman aşımına uğradı"
ErrorCode.ERR_FILE_NOT_FOUND="Dosya bulunamadı"
ErrorCode.ERR_FAILED="Bağlantı kurulamadı"
ErrorCode.ERR_NETWORK_CHANGED="Ağ değişti"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="SSL versiyon arayüzü. TLS 1.3 engellenmiş veya düzenlenmiş olabilir."
ErrorCode.ERR_SSL_PROTOCOL_ERROR="SSL protokol hatası. Güvenli iletişim sağlanamadı."
ErrorCode.ERR_CERT_DATE_INVALID="Sunucunun SSL sertifikası güncel değil veya bilgisayarınızın saati yanlış."
07070100000072000081A400000000000000000000000168D5836B0000015D000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/tt-RU.iniLocalFile="Локаль файл"
Width="Киңлек"
Height="Биеклек"
BrowserSource="Браузер"
Error.Code="Хата: %1"
Error.URL="Сылтама: %2"
ErrorCode.ERR_FILE_NOT_FOUND="Файл табылмады"
ErrorCode.ERR_FAILED="Тоташылып булмады"
ErrorCode.ERR_NETWORK_CHANGED="Челтәр үзгәртелде"
07070100000073000081A400000000000000000000000168D5836B00000D51000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/ug-CN.iniLocalFile="يەرلىك ھۆججەت"
URL="تور ئادرېس"
Width="كەڭلىك"
Height="ئېگىزلىك"
FPS="fps"
CSS="CSS نى خاسلاشتۇرۇش"
ShutdownSourceNotVisible="كۆرگىلى بولمىسا مەنبەنى تاقايدۇ"
RefreshBrowserActive="كۆرۈنۈش ئاكتىپلانغاندا توركۆرگۈنى يېڭىلايدۇ"
RefreshNoCache="نۆۋەتتىكى بەتنىڭ غەملىكىنى يېڭىلايدۇ"
BrowserSource="توركۆرگۈ"
CustomFrameRate="ئىختىيارى كاندۇك نىسىبىتىنى ئىشلىتىدۇ"
RerouteAudio="ئۈننى OBS ئارقىلىق تىزگىنلەيدۇ"
Inspect="تەكشۈر"
DevTools="توركۆرگۈ لەڭگەر «%1» نى تەكشۈرىدۇ"
CopyUrl="نۆۋەتتىكى ئادرېسنى كۆچۈر"
WebpageControlLevel="بەت ئىجازىتى"
WebpageControlLevel.Level.None="OBS زىيارەت ئىجازىتى يوق"
WebpageControlLevel.Level.ReadObs="OBS ھالەت ئۇچۇرىنى ئوقۇش ئىجازىتى"
WebpageControlLevel.Level.ReadUser="ئىشلەتكۈچى ئوچۇرىنى ئوقۇش ئىجازىتى (نۆۋەتتىكى كۆرۈنۈش توپلىمى، ئالمىشىش ئۈنۈمى)"
WebpageControlLevel.Level.Basic="OBS ئاساسىي زىيارەت ئىجازىتى (غەملەكنى قايتا قويۇشنى ساقلاش قاتارلىق)"
WebpageControlLevel.Level.Advanced="OBS ئالىي زىيارەت ئىجازىتى (كۆرۈنۈش ئۆزگەرتىش، غەملەكنى قايتا قويۇش باشلاش/توختىتىش قاتارلىق)"
WebpageControlLevel.Level.All="OBS تولۇق زىيارەت ئىجازىتى (ئاگاھلاندۇرمايلا ئېقىم باشلاش/توختىتىش قاتارلىق)"
Dialog.Alert="JavaScript ئاگاھلاندۇرۇش"
Dialog.Confirm="JavaScript جەزملەش"
Dialog.Prompt="JavaScript ئەسكەرتىش"
Dialog.BrowserDock="توركۆرگۈچ لەڭگەر"
Dialog.ReceivedFrom="«%1» تاپشۇرۇۋالدى"
Error.Title="ئۇ بەتنى يۈكلىيەلمەيدۇ!"
Error.Description="ئادرېسنىڭ توغرىلىقى ۋە تور بېكەتتە مەسىلە يوقلۇقىنى جەزملەڭ."
Error.Retry="چېكىلسە قايتا سىنايدۇ"
Error.Code="خاتالىق: %1"
Error.URL="تور ئادرېسى: %2"
Zoom.Reset="كېڭەيت تارايتنى ئەسلىگە قايتۇر"
Zoom.Out="يىراقلات"
Zoom.In="يېقىنلات"
ErrorCode.ERR_CONNECTION_REFUSED="مۇلازىمېتىر باغلىنىشنى رەت قىلدى"
ErrorCode.ERR_NAME_NOT_RESOLVED="مۇلازىمېتىرنىڭ IP ئادرېسىنى تاپالمىدى"
ErrorCode.ERR_CONNECTION_TIMED_OUT="باغلىنىش ۋاقتى توشتى"
ErrorCode.ERR_TIMED_OUT="مەشغۇلات ۋاقتى توشتى"
ErrorCode.ERR_FILE_NOT_FOUND="ھۆججەت تېپىلمىدى"
ErrorCode.ERR_FAILED="باغلىنالمىدى"
ErrorCode.ERR_NETWORK_CHANGED="تور ئۆزگەردى"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="SSL نەشر كاشىلا مەسىلىسى. TLS 1.3 چەكلەنگەن ياكى ئۆزگەرتىلگەن بولۇشى مۇمكىن."
ErrorCode.ERR_SSL_PROTOCOL_ERROR="SSL كېلىشىم خاتالىقى. بىخەتەر باغلىنىش قۇرالمىدى."
ErrorCode.ERR_CERT_DATE_INVALID="مۇلازىمېتىرنىڭ SSL گۇۋاھنامىسىنىڭ ۋاقتى ئۆتكەن ياكى كومپيۇتېرىڭىزنىڭ ۋاقتى خاتا."
07070100000074000081A400000000000000000000000168D5836B00000CDA000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/uk-UA.iniLocalFile="Локальний файл"
URL="URL-адреса"
Width="Ширина"
Height="Висота"
CSS="Власний CSS"
ShutdownSourceNotVisible="Вимикати джерело, коли воно не видиме"
RefreshBrowserActive="Оновити браузер, коли сцена стає активною"
RefreshNoCache="Оновити кеш поточної сторінки"
BrowserSource="Браузер"
CustomFrameRate="Використовувати власну частоту кадрів"
RerouteAudio="Керувати звуком через OBS"
Inspect="Дослідити"
DevTools="Дослідити Dock переглядача «%1»"
CopyUrl="Копіювати поточну адресу"
WebpageControlLevel="Дозволи сторінки"
WebpageControlLevel.Level.None="Немає доступу до OBS"
WebpageControlLevel.Level.ReadObs="Доступ на читання до інформації щодо стану OBS"
WebpageControlLevel.Level.ReadUser="Доступ до читання інформації користувача (поточна колекція сцен, переходи)"
WebpageControlLevel.Level.Basic="Базовий доступ до OBS (Зберегти буфер повтору тощо)"
WebpageControlLevel.Level.Advanced="Розширений доступ до OBS (Змінити сцени, Запуск/зупинка буферу повторів тощо)"
WebpageControlLevel.Level.All="Повний доступ до OBS (Запуск/зупинка трансляції без попереджень тощо)"
Dialog.Alert="Попередження JavaScript"
Dialog.Confirm="JavaScript підтвердження"
Dialog.Prompt="Запит JavaScript"
Dialog.BrowserDock="Док-панель браузера"
Dialog.ReceivedFrom="Отримано від '%1'"
Error.Title="Не вдалося завантажити цю сторінку!"
Error.Description="Переконайтеся, що адреса правильна, а сайт не має проблем."
Error.Retry="Натисніть сюди, щоб повторити спробу"
Error.Code="Помилка: %1"
Zoom.Reset="Скинути масштаб"
Zoom.Out="Зменшити"
Zoom.In="Збільшити"
ErrorCode.ERR_CONNECTION_REFUSED="Сервер відхилив з’єднання"
ErrorCode.ERR_NAME_NOT_RESOLVED="IP-адреси сервера не знайдено"
ErrorCode.ERR_CONNECTION_TIMED_OUT="Час очікування з'єднання минув"
ErrorCode.ERR_TIMED_OUT="Час очікування операції минув"
ErrorCode.ERR_FILE_NOT_FOUND="Файл не знайдено"
ErrorCode.ERR_FAILED="Не вдалося з'єднатися"
ErrorCode.ERR_NETWORK_CHANGED="Мережу змінено"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="Конфлікт версій SSL. Можливе блокування або змінення TLS 1.3."
ErrorCode.ERR_SSL_PROTOCOL_ERROR="Помилка протоколу SSL. Не вдалося здійснити безпечне з'єднання."
ErrorCode.ERR_CERT_DATE_INVALID="SSL-сертифікат сервера застарілий або на вашому комп'ютері встановлено хибний час."
07070100000075000081A400000000000000000000000168D5836B00000B49000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/ur-PK.iniLocalFile="مقامی فائل"
URL="یو آر ایل"
Width="چوڑائی"
Height="اونچائی"
FPS="فریم فی سیکنڈ"
CSS="حسب ضرورت CSS"
ShutdownSourceNotVisible="جب نظر نہ آئے تو شٹ ڈاؤن سورس"
RefreshBrowserActive="منظر فعال ہونے پر براؤزر کو ریفریش کریں"
RefreshNoCache="موجودہ صفحہ کے کیشے کو تازہ کریں"
BrowserSource="براؤزر"
CustomFrameRate="حسب ضرورت فریم ریٹ استعمال کریں"
RerouteAudio="OBS کے ذریعے آڈیو کو کنٹرول کریں"
WebpageControlLevel="صفحہ کی اجازت"
WebpageControlLevel.Level.None="OBS تک رسائی نہیں ہے"
WebpageControlLevel.Level.ReadObs="OBS کی حیثیت کی معلومات تک رسائی پڑھیں"
WebpageControlLevel.Level.ReadUser="صارف کی معلومات تک رسائی پڑھیں (موجودہ منظر کا مجموعہ، ٹرانزیشن)"
WebpageControlLevel.Level.Basic="OBS تک بنیادی رسائی (ری پلے بفر کو محفوظ کریں، وغیرہ)"
WebpageControlLevel.Level.Advanced="OBS تک اعلی درجے کی رسائی (مناظر کو تبدیل کریں، ری پلے بفر کو اسٹارٹ/سٹاپ کریں، وغیرہ)"
WebpageControlLevel.Level.All="OBS تک مکمل رسائی (بغیر انتباہ کے سلسلہ بندی شروع/بند کریں وغیرہ)"
Dialog.Alert="جاوا اسکرپٹ الرٹ"
Dialog.Confirm="جاوا اسکرپٹ کی تصدیق کریں"
Dialog.Prompt="جاوا اسکرپٹ پرامپٹ"
Dialog.BrowserDock="براؤزر ڈاک"
Dialog.ReceivedFrom="'%1' سے موصول ہوا"
Error.Title="وہ صفحہ لوڈ نہیں ہو سکا!"
Error.Description="یقینی بنائیں کہ پتہ درست ہے، اور یہ کہ سائٹ کو کوئی مسئلہ نہیں ہے۔"
Error.Retry="دوبارہ کوشش کرنے کے لیے یہاں کلک کریں"
Error.Code="خرابی: %1"
Error.URL="یو آر ایل: %2"
ErrorCode.ERR_CONNECTION_REFUSED="سرور نے کنکشن سے انکار کر دیا"
ErrorCode.ERR_NAME_NOT_RESOLVED="سرور کا IP پتہ نہیں ملا"
ErrorCode.ERR_CONNECTION_TIMED_OUT="کنکشن کا وقت ختم ہوگیا"
ErrorCode.ERR_TIMED_OUT="آپریشن کا وقت ختم ہو گیا"
ErrorCode.ERR_FILE_NOT_FOUND="فائل نہیں ملی"
ErrorCode.ERR_FAILED="کنکشن میں ناکام"
ErrorCode.ERR_NETWORK_CHANGED="نیٹ ورک بدل گیا"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="SSL ورژن میں مداخلت۔ TLS 1.3 کو بلاک یا تبدیل کیا جا سکتا ہے۔"
ErrorCode.ERR_SSL_PROTOCOL_ERROR="SSL پروٹوکول کی خرابی۔ محفوظ کنکشن نہیں بنا سکا۔"
ErrorCode.ERR_CERT_DATE_INVALID="سرور کا SSL سرٹیفکیٹ پرانا ہے یا آپ کے کمپیوٹر کا وقت غلط ہے۔"
07070100000076000081A400000000000000000000000168D5836B00000A4B000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/vi-VN.iniLocalFile="Tập tin trên máy"
Width="Chiều rộng"
Height="Chiều cao"
CSS="CSS tùy chỉnh"
ShutdownSourceNotVisible="Tắt máy khi không hiển thị"
RefreshBrowserActive="Làm mới trình duyệt khi cảnh bắt đầu hoạt động"
RefreshNoCache="Làm mới cache trang hiện tại"
BrowserSource="Trình duyệt"
CustomFrameRate="Dùng tốc độ khung hình tùy chọn"
RerouteAudio="Kiểm soát âm thanh qua OBS"
Inspect="Kiểm tra phần tử"
DevTools="Kiểm tra dock của trình duyệt '%1'"
CopyUrl="Sao chép địa chỉ hiện tại"
WebpageControlLevel="Quyền cho phép của trang web"
WebpageControlLevel.Level.None="Không được truy cập OBS"
WebpageControlLevel.Level.ReadObs="Cho phép đọc thông tin của OBS"
WebpageControlLevel.Level.ReadUser="Cho phép đọc thông tin của của người dùng (Bộ Sưu Tập Cảnh hiện tại và Chuyển Cảnh)"
WebpageControlLevel.Level.Basic="Truy cập cơ bản vào OBS (Lưu bộ đệm xem lại, v.v..)"
WebpageControlLevel.Level.Advanced="Truy cập nâng cao vào OBS (Thay đổi cảnh, Bắt đầu/Dừng bộ đệm phát lại, v.v..)"
WebpageControlLevel.Level.All="Truy cập toàn bộ vào OBS (Bắt đầu/Dừng phát luồng mà không có cảnh báo, v.v..)"
Dialog.Alert="JavaScript cảnh báo"
Dialog.Confirm="JavaScript xác nhận"
Dialog.Prompt="Khung chữ JavaScript"
Dialog.BrowserDock="Giá đỡ trình duyệt"
Dialog.ReceivedFrom="Nhận từ '%1'"
Error.Title="Không thể tải trang này!"
Error.Description="Hãy đảm bảo địa chỉ đúng, và trang đó không gặp vấn đề."
Error.Retry="Nhấn vào đây để tìm kiếm"
Error.Code="Lỗi: %1"
Zoom.Reset="Đặt lại thu phóng"
Zoom.Out="Thu nhỏ"
Zoom.In="Phóng to"
ErrorCode.ERR_CONNECTION_REFUSED="Máy chủ đã từ chối kết nối"
ErrorCode.ERR_NAME_NOT_RESOLVED="Không tìm thấy địa chỉ IP của máy chủ"
ErrorCode.ERR_CONNECTION_TIMED_OUT="Kết nối đã hết hạn"
ErrorCode.ERR_TIMED_OUT="Kết nối đã hết hạn"
ErrorCode.ERR_FILE_NOT_FOUND="Không tìm thấy tệp"
ErrorCode.ERR_FAILED="Không thể kết nối"
ErrorCode.ERR_NETWORK_CHANGED="Mạng đã bị thay đổi"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="Có sự can thiệp phiên bản SSL. TLS 1.3 có lẽ đã bị khoá lại hoặc chỉnh sửa."
ErrorCode.ERR_SSL_PROTOCOL_ERROR="Lỗi giao thức SSL. Không thể tạo một kết nối an toàn."
ErrorCode.ERR_CERT_DATE_INVALID="Giấy phép SSL của server đã hết hạn hoặc đồng hồ trên máy tính của bạn không có chính xác."
07070100000077000081A400000000000000000000000168D5836B00000826000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/zh-CN.iniLocalFile="本地文件"
Width="宽度"
Height="高度"
FPS="帧率"
CSS="自定义 CSS"
ShutdownSourceNotVisible="当不可见时关闭源"
RefreshBrowserActive="当场景变为活动状态时,刷新浏览器"
RefreshNoCache="刷新当前页面缓存"
BrowserSource="浏览器"
CustomFrameRate="使用自定义帧率"
RerouteAudio="通过OBS控制音频"
Inspect="检查"
DevTools="检查浏览器停靠窗口“'%1”"
CopyUrl="复制当前地址"
WebpageControlLevel="页面权限"
WebpageControlLevel.Level.None="没有对OBS的访问权限"
WebpageControlLevel.Level.ReadObs="OBS 状态信息的读取权限"
WebpageControlLevel.Level.ReadUser="用户信息的读取权限(当前场景集合、转场动画)"
WebpageControlLevel.Level.Basic="OBS 的基本访问权限(保存回放缓冲等)"
WebpageControlLevel.Level.Advanced="OBS 的高级访问权限(更改场景,启动或停止播放缓冲等)"
WebpageControlLevel.Level.All="OBS 的完全访问权限(启动或停止串流时不显示警告等)"
Dialog.Alert="JavaScript 警告"
Dialog.Confirm="JavaScript 确认"
Dialog.Prompt="JavaScript 提示"
Dialog.BrowserDock="浏览器停靠窗口"
Dialog.ReceivedFrom="接收自 '%1'"
Error.Title="无法加载该页面!"
Error.Description="请确保地址正确,且网站没有问题。"
Error.Retry="点此重试"
Error.Code="错误:%1"
Error.URL="网址: %2"
Zoom.Reset="重置缩放"
Zoom.Out="缩小"
Zoom.In="放大"
ErrorCode.ERR_CONNECTION_REFUSED="服务器拒绝了连接"
ErrorCode.ERR_NAME_NOT_RESOLVED="找不到服务器 IP 地址"
ErrorCode.ERR_CONNECTION_TIMED_OUT="连接超时"
ErrorCode.ERR_TIMED_OUT="操作超时"
ErrorCode.ERR_FILE_NOT_FOUND="找不到文件"
ErrorCode.ERR_FAILED="连接失败"
ErrorCode.ERR_NETWORK_CHANGED="发生网络变化"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="SSL 版本干扰问题。TLS 1.3 可能被阻止或修改。"
ErrorCode.ERR_SSL_PROTOCOL_ERROR="SSL 协议错误。无法建立安全连接。"
ErrorCode.ERR_CERT_DATE_INVALID="服务器的 SSL 证书已过期或您的计算机时间不正确。"
07070100000078000081A400000000000000000000000168D5836B0000085E000000000000000000000000000000000000004B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/data/locale/zh-TW.iniLocalFile="本機檔案"
URL="網址"
Width="寬度"
Height="高度"
FPS="FPS (影格率)"
CSS="自訂CSS"
ShutdownSourceNotVisible="不可見時關閉來源"
RefreshBrowserActive="取得焦點時更新瀏覽器"
RefreshNoCache="更新當前頁面快取"
BrowserSource="瀏覽器"
CustomFrameRate="使用自訂的畫面速率"
RerouteAudio="使用 OBS 控制音訊"
Inspect="檢查"
DevTools="檢查「%1」瀏覽器停駐視窗"
CopyUrl="複製目前地址"
WebpageControlLevel="頁面權限"
WebpageControlLevel.Level.None="沒有存取 OBS 的權限"
WebpageControlLevel.Level.ReadObs="對 OBS 狀態資訊有讀取權限"
WebpageControlLevel.Level.ReadUser="對使用者資訊有讀取權限(目前場景集、轉場效果)"
WebpageControlLevel.Level.Basic="對 OBS 只有基本存取權限(重放緩衝區等)"
WebpageControlLevel.Level.Advanced="對 OBS 有進階存取權限(切換場景、開始或停止播放緩衝區等)"
WebpageControlLevel.Level.All="對 OBS 有完整存取權限(逕行開始或停止串流而不先警告等等)"
Dialog.Alert="JavaScript 警告"
Dialog.Confirm="JavaScript 確認"
Dialog.Prompt="JavaScript 提示"
Dialog.BrowserDock="瀏覽器停駐視窗"
Dialog.ReceivedFrom="從「%1」來源收到"
Error.Title="無法載入頁面!"
Error.Description="請確定網址是否正確,且網站沒有出狀況。"
Error.Retry="按此重試"
Error.Code="錯誤:%1"
Error.URL="URL:%2"
Zoom.Reset="重設縮放"
Zoom.Out="縮小"
Zoom.In="放大"
ErrorCode.ERR_CONNECTION_REFUSED="伺服器拒絕連線"
ErrorCode.ERR_NAME_NOT_RESOLVED="找不到伺服器的 IP 地址"
ErrorCode.ERR_CONNECTION_TIMED_OUT="連線逾時"
ErrorCode.ERR_TIMED_OUT="操作逾時"
ErrorCode.ERR_FILE_NOT_FOUND="找不到檔案"
ErrorCode.ERR_FAILED="無法連線"
ErrorCode.ERR_NETWORK_CHANGED="網路已變更"
ErrorCode.ERR_SSL_VERSION_INTERFERENCE="SSL 版本發生干擾。TLS 1.3 可能已經被封鎖或修改過。"
ErrorCode.ERR_SSL_PROTOCOL_ERROR="SSL 協定通訊失敗。無法建立安全連線。"
ErrorCode.ERR_CERT_DATE_INVALID="伺服器的 SSL 憑證已經過期,或電腦時間不正確。"
07070100000079000041ED00000000000000000000000268D5836B00000000000000000000000000000000000000000000003A00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/deps0707010000007A000041ED00000000000000000000000268D5836B00000000000000000000000000000000000000000000004100000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/deps/base640707010000007B000081A400000000000000000000000168D5836B00000036000000000000000000000000000000000000004F00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/deps/base64/.clang-formatLanguage: Cpp
SortIncludes: false
DisableFormat: true
0707010000007C000081A400000000000000000000000168D5836B00000DE0000000000000000000000000000000000000004C00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/deps/base64/base64.cpp/*
base64.cpp and base64.h
Copyright (C) 2004-2008 René Nyffenegger
This source code is provided 'as-is', without any express or implied
warranty. In no event will the author be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this source code must not be misrepresented; you must not
claim that you wrote the original source code. If you use this source code
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original source code.
3. This notice may not be removed or altered from any source distribution.
René Nyffenegger rene.nyffenegger@adp-gmbh.ch
*/
#include "base64.hpp"
#include <iostream>
static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
static inline bool is_base64(unsigned char c) {
return (isalnum(c) || (c == '+') || (c == '/'));
}
std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
std::string ret;
int i = 0;
int j = 0;
unsigned char char_array_3[3];
unsigned char char_array_4[4];
while (in_len--) {
char_array_3[i++] = *(bytes_to_encode++);
if (i == 3) {
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for (i = 0; (i <4); i++)
ret += base64_chars[char_array_4[i]];
i = 0;
}
}
if (i)
{
for (j = i; j < 3; j++)
char_array_3[j] = '\0';
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for (j = 0; (j < i + 1); j++)
ret += base64_chars[char_array_4[j]];
while ((i++ < 3))
ret += '=';
}
return ret;
}
std::string base64_decode(std::string const& encoded_string) {
int in_len = (int)encoded_string.size();
int i = 0;
int j = 0;
int in_ = 0;
unsigned char char_array_4[4], char_array_3[3];
std::string ret;
while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
char_array_4[i++] = encoded_string[in_]; in_++;
if (i == 4) {
for (i = 0; i <4; i++)
char_array_4[i] = (unsigned char)base64_chars.find(char_array_4[i]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (i = 0; (i < 3); i++)
ret += char_array_3[i];
i = 0;
}
}
if (i) {
for (j = i; j <4; j++)
char_array_4[j] = 0;
for (j = 0; j <4; j++)
char_array_4[j] = (unsigned char)base64_chars.find(char_array_4[j]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
}
return ret;
}
0707010000007D000081A400000000000000000000000168D5836B00000192000000000000000000000000000000000000004C00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/deps/base64/base64.hpp#include <string>
std::string base64_encode(unsigned char const*, unsigned int len);
std::string base64_decode(std::string const& s);
static inline std::string base64_encode(const char *str, unsigned int len)
{
return base64_encode((unsigned const char *)str, len);
}
static inline std::string base64_encode(const std::string &str)
{
return base64_encode(str.c_str(), (unsigned int)str.size());
}
0707010000007E000081A400000000000000000000000168D5836B00000720000000000000000000000000000000000000004D00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/deps/signal-restore.cpp/******************************************************************************
Copyright (C) 2022 by Kyle Manning <tt2468@gmail.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
#include <signal.h>
#include <string.h>
#include "signal-restore.hpp"
// Method here borrowed from https://bitbucket.org/chromiumembedded/java-cef/src/master/native/signal_restore_posix.cpp
#ifndef _WIN32
template<typename T, size_t N> char (&ArraySizeHelper(T (&array)[N]))[N];
#define arraysize(array) (sizeof(ArraySizeHelper(array)))
const int signals_to_restore[] = {SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, SIGFPE, SIGSEGV,
SIGALRM, SIGTERM, SIGCHLD, SIGBUS, SIGTRAP, SIGPIPE};
struct sigaction signal_handlers[arraysize(signals_to_restore)];
void BackupSignalHandlers()
{
struct sigaction sigact;
for (unsigned i = 0; i < arraysize(signals_to_restore); ++i) {
memset(&sigact, 0, sizeof(sigact));
sigaction(signals_to_restore[i], nullptr, &sigact);
signal_handlers[i] = sigact;
}
}
void RestoreSignalHandlers()
{
for (unsigned i = 0; i < arraysize(signals_to_restore); ++i) {
sigaction(signals_to_restore[i], &signal_handlers[i], nullptr);
}
}
#endif
0707010000007F000081A400000000000000000000000168D5836B000003A7000000000000000000000000000000000000004D00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/deps/signal-restore.hpp/******************************************************************************
Copyright (C) 2022 by Kyle Manning <tt2468@gmail.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
#pragma once
#ifndef _WIN32
void BackupSignalHandlers();
void RestoreSignalHandlers();
#endif
07070100000080000081A400000000000000000000000168D5836B0000060E000000000000000000000000000000000000004A00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/deps/wide-string.cpp/******************************************************************************
Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
#include "wide-string.hpp"
#include <string.h>
#include <util/platform.h>
using namespace std;
wstring to_wide(const char *utf8)
{
if (!utf8 || !*utf8)
return wstring();
size_t isize = strlen(utf8);
size_t osize = os_utf8_to_wcs(utf8, isize, nullptr, 0);
if (!osize)
return wstring();
wstring wide;
wide.resize(osize);
os_utf8_to_wcs(utf8, isize, &wide[0], osize + 1);
return wide;
}
wstring to_wide(const std::string &utf8)
{
if (utf8.empty())
return wstring();
size_t osize = os_utf8_to_wcs(utf8.c_str(), utf8.size(), nullptr, 0);
if (!osize)
return wstring();
wstring wide;
wide.resize(osize);
os_utf8_to_wcs(utf8.c_str(), utf8.size(), &wide[0], osize + 1);
return wide;
}
07070100000081000081A400000000000000000000000168D5836B000003D0000000000000000000000000000000000000004A00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/deps/wide-string.hpp/******************************************************************************
Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
#pragma once
#include <string>
extern std::wstring to_wide(const char *utf8);
extern std::wstring to_wide(const std::string &utf8);
07070100000082000081A400000000000000000000000168D5836B0000057E000000000000000000000000000000000000004400000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/drm-format.cpp#include "drm-format.hpp"
#include <util/util.hpp>
#ifdef ENABLE_BROWSER_SHARED_TEXTURE
static const struct obs_cef_video_format supported_formats[] = {
{
CEF_COLOR_TYPE_RGBA_8888,
DRM_FORMAT_ABGR8888,
GS_RGBA_UNORM,
"RGBA_8888",
},
{
CEF_COLOR_TYPE_BGRA_8888,
DRM_FORMAT_ARGB8888,
GS_BGRA_UNORM,
"BGRA_8888",
},
};
constexpr size_t N_SUPPORTED_FORMATS = sizeof(supported_formats) / sizeof(supported_formats[0]);
bool obs_cef_all_drm_formats_supported(void)
{
size_t n_supported = 0;
size_t n_formats = 0;
enum gs_dmabuf_flags dmabuf_flags;
BPtr<uint32_t> drm_formats;
if (!gs_query_dmabuf_capabilities(&dmabuf_flags, &drm_formats, &n_formats))
return false;
for (size_t i = 0; i < n_formats; i++) {
for (size_t j = 0; j < N_SUPPORTED_FORMATS; j++) {
if (drm_formats[i] != supported_formats[j].drm_format)
continue;
blog(LOG_DEBUG, "[obs-browser]: CEF color type %s supported", supported_formats[j].pretty_name);
n_supported++;
}
}
return n_supported == N_SUPPORTED_FORMATS;
}
struct obs_cef_video_format obs_cef_format_from_cef_type(cef_color_type_t cef_type)
{
for (size_t i = 0; i < N_SUPPORTED_FORMATS; i++) {
if (supported_formats[i].cef_type == cef_type)
return supported_formats[i];
}
blog(LOG_ERROR, "[obs-browser]: Unsupported CEF color format (%d)", cef_type);
return {cef_type, DRM_FORMAT_INVALID, GS_UNKNOWN, NULL};
}
#endif
07070100000083000081A400000000000000000000000168D5836B000001AA000000000000000000000000000000000000004400000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/drm-format.hpp#pragma once
#include <graphics/graphics.h>
#include <libdrm/drm_fourcc.h>
#include "cef-headers.hpp"
#ifdef ENABLE_BROWSER_SHARED_TEXTURE
struct obs_cef_video_format {
cef_color_type_t cef_type;
uint32_t drm_format;
enum gs_color_format gs_format;
const char *pretty_name;
};
bool obs_cef_all_drm_formats_supported(void);
struct obs_cef_video_format obs_cef_format_from_cef_type(cef_color_type_t cef_type);
#endif
07070100000084000081A400000000000000000000000168D5836B00000446000000000000000000000000000000000000004700000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/helper-info.plist<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDisplayName</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>com.obsproject.obs-studio.helper${BUNDLE_ID_SUFFIX}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>LSEnvironment</key>
<dict>
<key>MallocNanoZone</key>
<string>0</string>
</dict>
<key>LSFileQuarantineEnabled</key>
<true/>
<key>LSMinimumSystemVersion</key>
<string>${MINIMUM_VERSION}</string>
<key>LSUIElement</key>
<string>1</string>
<key>NSSupportsAutomaticGraphicsSwitching</key>
<true/>
<key>NSHumanReadableCopyright</key>
<string>(c) 2012-${CURRENT_YEAR} Lain Bailey</string>
</dict>
</plist>
07070100000085000081A400000000000000000000000168D5836B00004662000000000000000000000000000000000000005000000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/linux-keyboard-helpers.hpp#pragma once
#define XK_3270
#include <X11/keysym.h>
#include <X11/XF86keysym.h>
#ifndef VKEY_UNKNOWN
#define VKEY_UNKNOWN 0
// POSIX specific VKEYs. Note that as of Windows SDK 7.1, 0x97-9F, 0xD8-DA,
// and 0xE8 are unassigned.
#define VKEY_WLAN 0x97
#define VKEY_POWER 0x98
#define VKEY_BRIGHTNESS_DOWN 0xD8
#define VKEY_BRIGHTNESS_UP 0xD9
#define VKEY_KBD_BRIGHTNESS_DOWN 0xDA
#define VKEY_KBD_BRIGHTNESS_UP 0xE8
// Windows does not have a specific key code for AltGr. We use the unused 0xE1
// (VK_OEM_AX) code to represent AltGr, matching the behaviour of Firefox on
// Linux.
#define VKEY_ALTGR 0xE1
// Windows does not have a specific key code for Compose. We use the unused
// 0xE6 (VK_ICO_CLEAR) code to represent Compose.
#define VKEY_COMPOSE 0xE6
// Left mouse button
#ifndef VKEY_LBUTTON
#define VKEY_LBUTTON 0x01
#endif
// Right mouse button
#ifndef VKEY_RBUTTON
#define VKEY_RBUTTON 0x02
#endif
// Middle mouse button (three-button mouse)
#ifndef VKEY_MBUTTON
#define VKEY_MBUTTON 0x04
#endif
#ifndef VKEY_XBUTTON1
#define VKEY_XBUTTON1 0x05
#endif
#ifndef VKEY_XBUTTON2
#define VKEY_XBUTTON2 0x06
#endif
#ifndef VKEY_BACK
#define VKEY_BACK 0x08
#endif
#ifndef VKEY_TAB
#define VKEY_TAB 0x09
#endif
#ifndef VKEY_CLEAR
#define VKEY_CLEAR 0x0C
#endif
#ifndef VKEY_RETURN
#define VKEY_RETURN 0x0D
#endif
#ifndef VKEY_SHIFT
#define VKEY_SHIFT 0x10
#endif
#ifndef VKEY_CONTROL
#define VKEY_CONTROL 0x11 // CTRL key
#endif
#ifndef VKEY_MENU
#define VKEY_MENU 0x12 // ALT key
#endif
#ifndef VKEY_PAUSE
#define VKEY_PAUSE 0x13 // PAUSE key
#endif
#ifndef VKEY_CAPITAL
#define VKEY_CAPITAL 0x14 // CAPS LOCK key
#endif
#ifndef VKEY_KANA
#define VKEY_KANA 0x15 // Input Method Editor (IME) Kana mode
#endif
#ifndef VKEY_HANGUL
#define VKEY_HANGUL 0x15 // IME Hangul mode
#endif
#ifndef VKEY_JUNJA
#define VKEY_JUNJA 0x17 // IME Junja mode
#endif
#ifndef VKEY_FINAL
#define VKEY_FINAL 0x18 // IME final mode
#endif
#ifndef VKEY_HANJA
#define VKEY_HANJA 0x19 // IME Hanja mode
#endif
#ifndef VKEY_KANJI
#define VKEY_KANJI 0x19 // IME Kanji mode
#endif
#ifndef VKEY_ESCAPE
#define VKEY_ESCAPE 0x1B // ESC key
#endif
#ifndef VKEY_CONVERT
#define VKEY_CONVERT 0x1C // IME convert
#endif
#ifndef VKEY_NONCONVERT
#define VKEY_NONCONVERT 0x1D // IME nonconvert
#endif
#ifndef VKEY_ACCEPT
#define VKEY_ACCEPT 0x1E // IME accept
#endif
#ifndef VKEY_MODECHANGE
#define VKEY_MODECHANGE 0x1F // IME mode change request
#endif
#ifndef VKEY_SPACE
#define VKEY_SPACE 0x20 // SPACE key
#endif
#ifndef VKEY_PRIOR
#define VKEY_PRIOR 0x21 // PAGE UP key
#endif
#ifndef VKEY_NEXT
#define VKEY_NEXT 0x22 // PAGE DOWN key
#endif
#ifndef VKEY_END
#define VKEY_END 0x23 // END key
#endif
#ifndef VKEY_HOME
#define VKEY_HOME 0x24 // HOME key
#endif
#ifndef VKEY_LEFT
#define VKEY_LEFT 0x25 // LEFT ARROW key
#endif
#ifndef VKEY_UP
#define VKEY_UP 0x26 // UP ARROW key
#endif
#ifndef VKEY_RIGHT
#define VKEY_RIGHT 0x27 // RIGHT ARROW key
#endif
#ifndef VKEY_DOWN
#define VKEY_DOWN 0x28 // DOWN ARROW key
#endif
#ifndef VKEY_SELECT
#define VKEY_SELECT 0x29 // SELECT key
#endif
#ifndef VKEY_PRINT
#define VKEY_PRINT 0x2A // PRINT key
#endif
#ifndef VKEY_EXECUTE
#define VKEY_EXECUTE 0x2B // EXECUTE key
#endif
#ifndef VKEY_SNAPSHOT
#define VKEY_SNAPSHOT 0x2C // PRINT SCREEN key
#endif
#ifndef VKEY_INSERT
#define VKEY_INSERT 0x2D // INS key
#endif
#ifndef VKEY_DELETE
#define VKEY_DELETE 0x2E // DEL key
#endif
#ifndef VKEY_HELP
#define VKEY_HELP 0x2F // HELP key
#endif
#define VKEY_0 0x30
#define VKEY_1 0x31
#define VKEY_2 0x32
#define VKEY_3 0x33
#define VKEY_4 0x34
#define VKEY_5 0x35
#define VKEY_6 0x36
#define VKEY_7 0x37
#define VKEY_8 0x38
#define VKEY_9 0x39
#define VKEY_A 0x41
#define VKEY_B 0x42
#define VKEY_C 0x43
#define VKEY_D 0x44
#define VKEY_E 0x45
#define VKEY_F 0x46
#define VKEY_G 0x47
#define VKEY_H 0x48
#define VKEY_I 0x49
#define VKEY_J 0x4A
#define VKEY_K 0x4B
#define VKEY_L 0x4C
#define VKEY_M 0x4D
#define VKEY_N 0x4E
#define VKEY_O 0x4F
#define VKEY_P 0x50
#define VKEY_Q 0x51
#define VKEY_R 0x52
#define VKEY_S 0x53
#define VKEY_T 0x54
#define VKEY_U 0x55
#define VKEY_V 0x56
#define VKEY_W 0x57
#define VKEY_X 0x58
#define VKEY_Y 0x59
#define VKEY_Z 0x5A
#define VKEY_LWIN 0x5B // Left Windows key (Microsoft Natural keyboard)
#define VKEY_RWIN 0x5C // Right Windows key (Natural keyboard)
#define VKEY_APPS 0x5D // Applications key (Natural keyboard)
#define VKEY_SLEEP 0x5F // Computer Sleep key
// Num pad keys
#define VKEY_NUMPAD0 0x60
#define VKEY_NUMPAD1 0x61
#define VKEY_NUMPAD2 0x62
#define VKEY_NUMPAD3 0x63
#define VKEY_NUMPAD4 0x64
#define VKEY_NUMPAD5 0x65
#define VKEY_NUMPAD6 0x66
#define VKEY_NUMPAD7 0x67
#define VKEY_NUMPAD8 0x68
#define VKEY_NUMPAD9 0x69
#define VKEY_MULTIPLY 0x6A
#define VKEY_ADD 0x6B
#define VKEY_SEPARATOR 0x6C
#define VKEY_SUBTRACT 0x6D
#define VKEY_DECIMAL 0x6E
#define VKEY_DIVIDE 0x6F
#define VKEY_F1 0x70
#define VKEY_F2 0x71
#define VKEY_F3 0x72
#define VKEY_F4 0x73
#define VKEY_F5 0x74
#define VKEY_F6 0x75
#define VKEY_F7 0x76
#define VKEY_F8 0x77
#define VKEY_F9 0x78
#define VKEY_F10 0x79
#define VKEY_F11 0x7A
#define VKEY_F12 0x7B
#define VKEY_F13 0x7C
#define VKEY_F14 0x7D
#define VKEY_F15 0x7E
#define VKEY_F16 0x7F
#define VKEY_F17 0x80
#define VKEY_F18 0x81
#define VKEY_F19 0x82
#define VKEY_F20 0x83
#define VKEY_F21 0x84
#define VKEY_F22 0x85
#define VKEY_F23 0x86
#define VKEY_F24 0x87
#define VKEY_NUMLOCK 0x90
#define VKEY_SCROLL 0x91
#define VKEY_LSHIFT 0xA0
#define VKEY_RSHIFT 0xA1
#define VKEY_LCONTROL 0xA2
#define VKEY_RCONTROL 0xA3
#define VKEY_LMENU 0xA4
#define VKEY_RMENU 0xA5
#define VKEY_BROWSER_BACK 0xA6 // Windows 2000/XP: Browser Back key
#define VKEY_BROWSER_FORWARD 0xA7 // Windows 2000/XP: Browser Forward key
#define VKEY_BROWSER_REFRESH 0xA8 // Windows 2000/XP: Browser Refresh key
#define VKEY_BROWSER_STOP 0xA9 // Windows 2000/XP: Browser Stop key
#define VKEY_BROWSER_SEARCH 0xAA // Windows 2000/XP: Browser Search key
#define VKEY_BROWSER_FAVORITES 0xAB // Windows 2000/XP: Browser Favorites key
#define VKEY_BROWSER_HOME 0xAC // Windows 2000/XP: Browser Start and Home key
#define VKEY_VOLUME_MUTE 0xAD // Windows 2000/XP: Volume Mute key
#define VKEY_VOLUME_DOWN 0xAE // Windows 2000/XP: Volume Down key
#define VKEY_VOLUME_UP 0xAF // Windows 2000/XP: Volume Up key
#define VKEY_MEDIA_NEXT_TRACK 0xB0 // Windows 2000/XP: Next Track key
#define VKEY_MEDIA_PREV_TRACK 0xB1 // Windows 2000/XP: Previous Track key
#define VKEY_MEDIA_STOP 0xB2 // Windows 2000/XP: Stop Media key
#define VKEY_MEDIA_PLAY_PAUSE 0xB3 // Windows 2000/XP: Play/Pause Media key
#define VKEY_MEDIA_LAUNCH_MAIL 0xB4 // Windows 2000/XP: Start Mail key
#define VKEY_MEDIA_LAUNCH_MEDIA_SELECT 0xB5 // Windows 2000/XP: Select Media key
#define VKEY_MEDIA_LAUNCH_APP1 0xB6 // VKEY_LAUNCH_APP1 (B6) Windows 2000/XP: Start Application 1 key
#define VKEY_MEDIA_LAUNCH_APP2 0xB7 // VKEY_LAUNCH_APP2 (B7) Windows 2000/XP: Start Application 2 key
// VKEY_OEM_1 (BA) Used for miscellaneous characters; it can vary by keyboard.
// Windows 2000/XP: For the US standard keyboard, the ';:' key
#define VKEY_OEM_1 0xBA
// Windows 2000/XP: For any country/region, the '+' key
#define VKEY_OEM_PLUS 0xBB
// Windows 2000/XP: For any country/region, the ',' key
#define VKEY_OEM_COMMA 0xBC
// Windows 2000/XP: For any country/region, the '-' key
#define VKEY_OEM_MINUS 0xBD
// Windows 2000/XP: For any country/region, the '.' key
#define VKEY_OEM_PERIOD 0xBE
// VKEY_OEM_2 (BF) Used for miscellaneous characters; it can vary by keyboard.
// Windows 2000/XP: For the US standard keyboard, the '/?' key
#define VKEY_OEM_2 0xBF
// VKEY_OEM_3 (C0) Used for miscellaneous characters; it can vary by keyboard.
// Windows 2000/XP: For the US standard keyboard, the '`~' key
#define VKEY_OEM_3 0xC0
// VKEY_OEM_4 (DB) Used for miscellaneous characters; it can vary by keyboard.
// Windows 2000/XP: For the US standard keyboard, the '[{' key
#define VKEY_OEM_4 0xDB
// VKEY_OEM_5 (DC) Used for miscellaneous characters; it can vary by keyboard.
// Windows 2000/XP: For the US standard keyboard, the '\|' key
#define VKEY_OEM_5 0xDC
// VKEY_OEM_6 (DD) Used for miscellaneous characters; it can vary by keyboard.
// Windows 2000/XP: For the US standard keyboard, the ']}' key
#define VKEY_OEM_6 0xDD
// VKEY_OEM_7 (DE) Used for miscellaneous characters; it can vary by keyboard.
// Windows 2000/XP: For the US standard keyboard, the
// 'single-quote/double-quote' key
#define VKEY_OEM_7 0xDE
// VKEY_OEM_8 (DF) Used for miscellaneous characters; it can vary by keyboard.
#define VKEY_OEM_8 0xDF
// VKEY_OEM_102 (E2) Windows 2000/XP: Either the angle bracket key or the
// backslash key on the RT 102-key keyboard
#define VKEY_OEM_102 0xE2
#define VKEY_OEM_BACKTAB 0xF5
#define VKEY_OEM_FJ_TOUROKU 0x94
#define VKEY_OEM_FJ_MASSHOU 0x93
// Windows 95/98/Me, Windows NT 4.0, Windows 2000/XP: IME PROCESS key
#define VKEY_PROCESSKEY 0xE5
// Windows 2000/XP: Used to pass Unicode characters as if they were keystrokes.
// The VKEY_PACKET key is the low word of a 32-bit Virtual Key value used for
// non-keyboard input methods. For more information, see Remark in
// KEYBDINPUT,SendInput, WM_KEYDOWN, and WM_KEYUP
#define VKEY_PACKET 0xE7
#define VKEY_ATTN 0xF6 // Attn key
#define VKEY_CRSEL 0xF7 // CrSel key
#define VKEY_EXSEL 0xF8 // ExSel key
#define VKEY_EREOF 0xF9 // Erase EOF key
#define VKEY_PLAY 0xFA // Play key
#define VKEY_ZOOM 0xFB // Zoom key
#define VKEY_NONAME 0xFC // Reserved for future use
#define VKEY_PA1 0xFD // VKEY_PA1 (FD) PA1 key
#define VKEY_OEM_CLEAR 0xFE // Clear key
#endif // VKEY_UNKNOWN
/* ------------------------------------------------------------------------- */
static uint32_t KeyboardCodeFromXKeysym(unsigned int keysym)
{
switch (keysym) {
case XK_BackSpace:
return VKEY_BACK;
case XK_Delete:
case XK_KP_Delete:
return VKEY_DELETE;
case XK_Tab:
case XK_KP_Tab:
case XK_ISO_Left_Tab:
case XK_3270_BackTab:
return VKEY_TAB;
case XK_Linefeed:
case XK_Return:
case XK_KP_Enter:
case XK_ISO_Enter:
return VKEY_RETURN;
case XK_Clear:
case XK_KP_Begin: // NumPad 5 without Num Lock, for crosbug.com/29169.
return VKEY_CLEAR;
case XK_KP_Space:
case XK_space:
return VKEY_SPACE;
case XK_Home:
case XK_KP_Home:
return VKEY_HOME;
case XK_End:
case XK_KP_End:
return VKEY_END;
case XK_Page_Up:
case XK_KP_Page_Up: // aka XK_KP_Prior
return VKEY_PRIOR;
case XK_Page_Down:
case XK_KP_Page_Down: // aka XK_KP_Next
return VKEY_NEXT;
case XK_Left:
case XK_KP_Left:
return VKEY_LEFT;
case XK_Right:
case XK_KP_Right:
return VKEY_RIGHT;
case XK_Down:
case XK_KP_Down:
return VKEY_DOWN;
case XK_Up:
case XK_KP_Up:
return VKEY_UP;
case XK_Escape:
return VKEY_ESCAPE;
case XK_Kana_Lock:
case XK_Kana_Shift:
return VKEY_KANA;
case XK_Hangul:
return VKEY_HANGUL;
case XK_Hangul_Hanja:
return VKEY_HANJA;
case XK_Kanji:
return VKEY_KANJI;
case XK_Henkan:
return VKEY_CONVERT;
case XK_Muhenkan:
return VKEY_NONCONVERT;
case XK_A:
case XK_a:
return VKEY_A;
case XK_B:
case XK_b:
return VKEY_B;
case XK_C:
case XK_c:
return VKEY_C;
case XK_D:
case XK_d:
return VKEY_D;
case XK_E:
case XK_e:
return VKEY_E;
case XK_F:
case XK_f:
return VKEY_F;
case XK_G:
case XK_g:
return VKEY_G;
case XK_H:
case XK_h:
return VKEY_H;
case XK_I:
case XK_i:
return VKEY_I;
case XK_J:
case XK_j:
return VKEY_J;
case XK_K:
case XK_k:
return VKEY_K;
case XK_L:
case XK_l:
return VKEY_L;
case XK_M:
case XK_m:
return VKEY_M;
case XK_N:
case XK_n:
return VKEY_N;
case XK_O:
case XK_o:
return VKEY_O;
case XK_P:
case XK_p:
return VKEY_P;
case XK_Q:
case XK_q:
return VKEY_Q;
case XK_R:
case XK_r:
return VKEY_R;
case XK_S:
case XK_s:
return VKEY_S;
case XK_T:
case XK_t:
return VKEY_T;
case XK_U:
case XK_u:
return VKEY_U;
case XK_V:
case XK_v:
return VKEY_V;
case XK_W:
case XK_w:
return VKEY_W;
case XK_X:
case XK_x:
return VKEY_X;
case XK_Y:
case XK_y:
return VKEY_Y;
case XK_Z:
case XK_z:
return VKEY_Z;
case XK_0:
case XK_1:
case XK_2:
case XK_3:
case XK_4:
case XK_5:
case XK_6:
case XK_7:
case XK_8:
case XK_9:
return static_cast<unsigned int>(VKEY_0 + (keysym - XK_0));
case XK_parenright:
return VKEY_0;
case XK_exclam:
return VKEY_1;
case XK_at:
return VKEY_2;
case XK_numbersign:
return VKEY_3;
case XK_dollar:
return VKEY_4;
case XK_percent:
return VKEY_5;
case XK_asciicircum:
return VKEY_6;
case XK_ampersand:
return VKEY_7;
case XK_asterisk:
return VKEY_8;
case XK_parenleft:
return VKEY_9;
case XK_KP_0:
case XK_KP_1:
case XK_KP_2:
case XK_KP_3:
case XK_KP_4:
case XK_KP_5:
case XK_KP_6:
case XK_KP_7:
case XK_KP_8:
case XK_KP_9:
return static_cast<unsigned int>(VKEY_NUMPAD0 + (keysym - XK_KP_0));
case XK_multiply:
case XK_KP_Multiply:
return VKEY_MULTIPLY;
case XK_KP_Add:
return VKEY_ADD;
case XK_KP_Separator:
return VKEY_SEPARATOR;
case XK_KP_Subtract:
return VKEY_SUBTRACT;
case XK_KP_Decimal:
return VKEY_DECIMAL;
case XK_KP_Divide:
return VKEY_DIVIDE;
case XK_KP_Equal:
case XK_equal:
case XK_plus:
return VKEY_OEM_PLUS;
case XK_comma:
case XK_less:
return VKEY_OEM_COMMA;
case XK_minus:
case XK_underscore:
return VKEY_OEM_MINUS;
case XK_greater:
case XK_period:
return VKEY_OEM_PERIOD;
case XK_colon:
case XK_semicolon:
return VKEY_OEM_1;
case XK_question:
case XK_slash:
return VKEY_OEM_2;
case XK_asciitilde:
case XK_quoteleft:
return VKEY_OEM_3;
case XK_bracketleft:
case XK_braceleft:
return VKEY_OEM_4;
case XK_backslash:
case XK_bar:
return VKEY_OEM_5;
case XK_bracketright:
case XK_braceright:
return VKEY_OEM_6;
case XK_quoteright:
case XK_quotedbl:
return VKEY_OEM_7;
case XK_ISO_Level5_Shift:
return VKEY_OEM_8;
case XK_Shift_L:
case XK_Shift_R:
return VKEY_SHIFT;
case XK_Control_L:
case XK_Control_R:
return VKEY_CONTROL;
case XK_Meta_L:
case XK_Meta_R:
case XK_Alt_L:
case XK_Alt_R:
return VKEY_MENU;
case XK_ISO_Level3_Shift:
return VKEY_ALTGR;
case XK_Multi_key:
return VKEY_COMPOSE;
case XK_Pause:
return VKEY_PAUSE;
case XK_Caps_Lock:
return VKEY_CAPITAL;
case XK_Num_Lock:
return VKEY_NUMLOCK;
case XK_Scroll_Lock:
return VKEY_SCROLL;
case XK_Select:
return VKEY_SELECT;
case XK_Print:
return VKEY_PRINT;
case XK_Execute:
return VKEY_EXECUTE;
case XK_Insert:
case XK_KP_Insert:
return VKEY_INSERT;
case XK_Help:
return VKEY_HELP;
case XK_Super_L:
return VKEY_LWIN;
case XK_Super_R:
return VKEY_RWIN;
case XK_Menu:
return VKEY_APPS;
case XK_F1:
case XK_F2:
case XK_F3:
case XK_F4:
case XK_F5:
case XK_F6:
case XK_F7:
case XK_F8:
case XK_F9:
case XK_F10:
case XK_F11:
case XK_F12:
case XK_F13:
case XK_F14:
case XK_F15:
case XK_F16:
case XK_F17:
case XK_F18:
case XK_F19:
case XK_F20:
case XK_F21:
case XK_F22:
case XK_F23:
case XK_F24:
return static_cast<unsigned int>(VKEY_F1 + (keysym - XK_F1));
case XK_KP_F1:
case XK_KP_F2:
case XK_KP_F3:
case XK_KP_F4:
return static_cast<unsigned int>(VKEY_F1 + (keysym - XK_KP_F1));
case XK_guillemotleft:
case XK_guillemotright:
case XK_degree:
// In the case of canadian multilingual keyboard layout, VKEY_OEM_102
// is assigned to ugrave key.
case XK_ugrave:
case XK_Ugrave:
case XK_brokenbar:
// international backslash key in 102 keyboard
return VKEY_OEM_102;
// When evdev is in use, /usr/share/X11/xkb/symbols/inet maps F13-18
// keys to the special XF86XK symbols to support Microsoft Ergonomic
// keyboards: https://bugs.freedesktop.org/show_bug.cgi?id=5783 In
// Chrome, we map these X key symbols back to F13-18 since we don't
// have VKEYs for these XF86XK symbols.
case XF86XK_Tools:
return VKEY_F13;
case XF86XK_Launch5:
return VKEY_F14;
case XF86XK_Launch6:
return VKEY_F15;
case XF86XK_Launch7:
return VKEY_F16;
case XF86XK_Launch8:
return VKEY_F17;
case XF86XK_Launch9:
return VKEY_F18;
case XF86XK_Refresh:
case XF86XK_History:
case XF86XK_OpenURL:
case XF86XK_AddFavorite:
case XF86XK_Go:
case XF86XK_ZoomIn:
case XF86XK_ZoomOut:
// ui::AcceleratorGtk tries to convert the XF86XK_ keysyms on
// Chrome startup. It's safe to return VKEY_UNKNOWN here since
// ui::AcceleratorGtk also checks a Gdk keysym.
// http://crbug.com/109843
return VKEY_UNKNOWN;
// For supporting multimedia buttons on a USB keyboard.
case XF86XK_Back:
return VKEY_BROWSER_BACK;
case XF86XK_Forward:
return VKEY_BROWSER_FORWARD;
case XF86XK_Reload:
return VKEY_BROWSER_REFRESH;
case XF86XK_Stop:
return VKEY_BROWSER_STOP;
case XF86XK_Search:
return VKEY_BROWSER_SEARCH;
case XF86XK_Favorites:
return VKEY_BROWSER_FAVORITES;
case XF86XK_HomePage:
return VKEY_BROWSER_HOME;
case XF86XK_AudioMute:
return VKEY_VOLUME_MUTE;
case XF86XK_AudioLowerVolume:
return VKEY_VOLUME_DOWN;
case XF86XK_AudioRaiseVolume:
return VKEY_VOLUME_UP;
case XF86XK_AudioNext:
return VKEY_MEDIA_NEXT_TRACK;
case XF86XK_AudioPrev:
return VKEY_MEDIA_PREV_TRACK;
case XF86XK_AudioStop:
return VKEY_MEDIA_STOP;
case XF86XK_AudioPlay:
return VKEY_MEDIA_PLAY_PAUSE;
case XF86XK_Mail:
return VKEY_MEDIA_LAUNCH_MAIL;
case XF86XK_LaunchA: // F3 on an Apple keyboard.
return VKEY_MEDIA_LAUNCH_APP1;
case XF86XK_LaunchB: // F4 on an Apple keyboard.
case XF86XK_Calculator:
return VKEY_MEDIA_LAUNCH_APP2;
case XF86XK_WLAN:
return VKEY_WLAN;
case XF86XK_PowerOff:
return VKEY_POWER;
case XF86XK_MonBrightnessDown:
return VKEY_BRIGHTNESS_DOWN;
case XF86XK_MonBrightnessUp:
return VKEY_BRIGHTNESS_UP;
case XF86XK_KbdBrightnessDown:
return VKEY_KBD_BRIGHTNESS_DOWN;
case XF86XK_KbdBrightnessUp:
return VKEY_KBD_BRIGHTNESS_UP;
// TODO(sad): some keycodes are still missing.
}
return VKEY_UNKNOWN;
}
07070100000086000041ED00000000000000000000000268D5836B00000000000000000000000000000000000000000000004600000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/obs-browser-page07070100000087000081A400000000000000000000000168D5836B00000456000000000000000000000000000000000000004F00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/obs-browser-page.manifest<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<description>OBS Browser Page (CEF)</description>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel
level="asInvoker"
uiAccess="false"
/>
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<maxversiontested Id="10.0.19041.0"/>
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
</application>
</compatibility>
</assembly>07070100000088000081A400000000000000000000000168D5836B0000114B000000000000000000000000000000000000006000000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/obs-browser-page/obs-browser-page-main.cpp/******************************************************************************
Copyright (C) 2014 by John R. Bradley <jrb@turrettech.com>
Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
#include "cef-headers.hpp"
#include "browser-app.hpp"
#ifdef _WIN32
#include <windows.h>
#include <string>
#include <thread>
// GPU hint exports for AMD/NVIDIA laptops
#ifdef _MSC_VER
extern "C" __declspec(dllexport) DWORD NvOptimusEnablement = 1;
extern "C" __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
#endif
static HANDLE shutdown_event = nullptr;
static bool thread_initialized = false;
DECLARE_HANDLE(OBS_DPI_AWARENESS_CONTEXT);
#define OBS_DPI_AWARENESS_CONTEXT_UNAWARE ((OBS_DPI_AWARENESS_CONTEXT)-1)
#define OBS_DPI_AWARENESS_CONTEXT_SYSTEM_AWARE ((OBS_DPI_AWARENESS_CONTEXT)-2)
#define OBS_DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE ((OBS_DPI_AWARENESS_CONTEXT)-3)
#define OBS_DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((OBS_DPI_AWARENESS_CONTEXT)-4)
static bool SetHighDPIv2Scaling()
{
static BOOL(WINAPI * func)(OBS_DPI_AWARENESS_CONTEXT) = nullptr;
func = reinterpret_cast<decltype(func)>(
GetProcAddress(GetModuleHandleW(L"USER32"), "SetProcessDpiAwarenessContext"));
if (!func) {
return false;
}
return !!func(OBS_DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
}
static void shutdown_check_thread(DWORD parent_pid, DWORD main_thread_id)
{
HANDLE parent = OpenProcess(SYNCHRONIZE, false, parent_pid);
if (!parent) {
return;
}
HANDLE handles[2] = {parent, shutdown_event};
DWORD ret = WaitForMultipleObjects(2, handles, false, INFINITE);
if (ret == WAIT_OBJECT_0) {
PostThreadMessage(main_thread_id, WM_QUIT, 0, 0);
ret = WaitForSingleObject(shutdown_event, 5000);
if (ret != WAIT_OBJECT_0) {
TerminateProcess(GetCurrentProcess(), (UINT)-1);
}
}
CloseHandle(parent);
}
int CALLBACK WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
PROCESS_POWER_THROTTLING_STATE PowerThrottling;
PowerThrottling.Version = PROCESS_POWER_THROTTLING_CURRENT_VERSION;
PowerThrottling.ControlMask = PROCESS_POWER_THROTTLING_IGNORE_TIMER_RESOLUTION;
PowerThrottling.StateMask = 0;
SetProcessInformation(GetCurrentProcess(), ProcessPowerThrottling, &PowerThrottling, sizeof(PowerThrottling));
std::thread shutdown_check;
CefMainArgs mainArgs(nullptr);
#if CHROME_VERSION_BUILD < 5615
if (!SetHighDPIv2Scaling())
CefEnableHighDPISupport();
#endif
CefRefPtr<CefCommandLine> command_line = CefCommandLine::CreateCommandLine();
command_line->InitFromString(::GetCommandLineW());
std::string parent_pid_str = command_line->GetSwitchValue("parent_pid");
std::string current_pid = std::to_string(GetCurrentProcessId());
if (!parent_pid_str.empty()) {
shutdown_event = CreateEvent(nullptr, true, false, nullptr);
DWORD parent_pid = (DWORD)std::stoi(parent_pid_str);
shutdown_check = std::thread(shutdown_check_thread, parent_pid, GetCurrentThreadId());
thread_initialized = true;
}
#else
#if defined(NO_STACK_PROTECTOR)
NO_STACK_PROTECTOR
#endif
int main(int argc, char *argv[])
{
#if defined(__APPLE__) && !defined(ENABLE_BROWSER_LEGACY)
CefScopedLibraryLoader library_loader;
if (!library_loader.LoadInHelper())
return 1;
#endif
CefMainArgs mainArgs(argc, argv);
#endif
CefRefPtr<BrowserApp> mainApp(new BrowserApp());
int ret = CefExecuteProcess(mainArgs, mainApp.get(), NULL);
#ifdef _WIN32
/* chromium browser subprocesses actually have TerminateProcess called
* on them for whatever reason, so it's unlikely this code will ever
* get called, but better to be safe than sorry */
if (thread_initialized) {
SetEvent(shutdown_event);
shutdown_check.join();
}
if (shutdown_event) {
CloseHandle(shutdown_event);
}
#endif
return ret;
}
07070100000089000081A400000000000000000000000168D5836B00005EF6000000000000000000000000000000000000004C00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/obs-browser-plugin.cpp/******************************************************************************
Copyright (C) 2014 by John R. Bradley <jrb@turrettech.com>
Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
#include <obs-frontend-api.h>
#include <util/threading.h>
#include <util/platform.h>
#include <util/util.hpp>
#include <util/dstr.hpp>
#include <obs-module.h>
#include <obs.hpp>
#include <functional>
#include <sstream>
#include <thread>
#include <mutex>
#include <nlohmann/json.hpp>
#include <obs-websocket-api.h>
#include "obs-browser-source.hpp"
#include "browser-scheme.hpp"
#include "browser-app.hpp"
#include "browser-version.h"
#include "cef-headers.hpp"
#ifdef _WIN32
#include <util/windows/ComPtr.hpp>
#include <dxgi.h>
#include <dxgi1_2.h>
#include <d3d11.h>
#else
#include "signal-restore.hpp"
#endif
#ifdef ENABLE_WAYLAND
#include <obs-nix-platform.h>
#endif
#ifdef ENABLE_BROWSER_QT_LOOP
#include <QApplication>
#include <QThread>
#endif
#if !defined(_WIN32) && !defined(__APPLE__)
#include "drm-format.hpp"
#endif
OBS_DECLARE_MODULE()
OBS_MODULE_USE_DEFAULT_LOCALE("obs-browser", "en-US")
MODULE_EXPORT const char *obs_module_description(void)
{
return "CEF-based web browser source & panels";
}
using namespace std;
static thread manager_thread;
static bool manager_initialized = false;
os_event_t *cef_started_event = nullptr;
#if defined(_WIN32)
static int adapterCount = 0;
#endif
static std::wstring deviceId;
bool hwaccel = false;
/* ========================================================================= */
#ifdef ENABLE_BROWSER_QT_LOOP
extern MessageObject messageObject;
#endif
class BrowserTask : public CefTask {
public:
std::function<void()> task;
inline BrowserTask(std::function<void()> task_) : task(task_) {}
virtual void Execute() override
{
#ifdef ENABLE_BROWSER_QT_LOOP
/* you have to put the tasks on the Qt event queue after this
* call otherwise the CEF message pump may stop functioning
* correctly, it's only supposed to take 10ms max */
QMetaObject::invokeMethod(&messageObject, "ExecuteTask", Qt::QueuedConnection,
Q_ARG(MessageTask, task));
#else
task();
#endif
}
IMPLEMENT_REFCOUNTING(BrowserTask);
};
bool QueueCEFTask(std::function<void()> task)
{
return CefPostTask(TID_UI, CefRefPtr<BrowserTask>(new BrowserTask(task)));
}
/* ========================================================================= */
static const char *default_css = "\
body { \
background-color: rgba(0, 0, 0, 0); \
margin: 0px auto; \
overflow: hidden; \
}";
static void browser_source_get_defaults(obs_data_t *settings)
{
obs_data_set_default_string(settings, "url", "https://obsproject.com/browser-source");
obs_data_set_default_int(settings, "width", 800);
obs_data_set_default_int(settings, "height", 600);
obs_data_set_default_int(settings, "fps", 30);
#ifdef ENABLE_BROWSER_SHARED_TEXTURE
obs_data_set_default_bool(settings, "fps_custom", false);
#else
obs_data_set_default_bool(settings, "fps_custom", true);
#endif
obs_data_set_default_bool(settings, "shutdown", false);
obs_data_set_default_bool(settings, "restart_when_active", false);
obs_data_set_default_int(settings, "webpage_control_level", (int)DEFAULT_CONTROL_LEVEL);
obs_data_set_default_string(settings, "css", default_css);
obs_data_set_default_bool(settings, "reroute_audio", false);
}
static bool is_local_file_modified(obs_properties_t *props, obs_property_t *, obs_data_t *settings)
{
bool enabled = obs_data_get_bool(settings, "is_local_file");
obs_property_t *url = obs_properties_get(props, "url");
obs_property_t *local_file = obs_properties_get(props, "local_file");
obs_property_set_visible(url, !enabled);
obs_property_set_visible(local_file, enabled);
return true;
}
static bool is_fps_custom(obs_properties_t *props, obs_property_t *, obs_data_t *settings)
{
bool enabled = obs_data_get_bool(settings, "fps_custom");
obs_property_t *fps = obs_properties_get(props, "fps");
obs_property_set_visible(fps, enabled);
return true;
}
static obs_properties_t *browser_source_get_properties(void *data)
{
obs_properties_t *props = obs_properties_create();
BrowserSource *bs = static_cast<BrowserSource *>(data);
DStr path;
obs_properties_set_flags(props, OBS_PROPERTIES_DEFER_UPDATE);
obs_property_t *prop = obs_properties_add_bool(props, "is_local_file", obs_module_text("LocalFile"));
if (bs && !bs->url.empty()) {
const char *slash;
dstr_copy(path, bs->url.c_str());
dstr_replace(path, "\\", "/");
slash = strrchr(path->array, '/');
if (slash)
dstr_resize(path, slash - path->array + 1);
}
obs_property_set_modified_callback(prop, is_local_file_modified);
obs_properties_add_path(props, "local_file", obs_module_text("LocalFile"), OBS_PATH_FILE, "*.*", path->array);
obs_properties_add_text(props, "url", obs_module_text("URL"), OBS_TEXT_DEFAULT);
obs_properties_add_int(props, "width", obs_module_text("Width"), 1, 8192, 1);
obs_properties_add_int(props, "height", obs_module_text("Height"), 1, 8192, 1);
obs_properties_add_bool(props, "reroute_audio", obs_module_text("RerouteAudio"));
obs_property_t *fps_set = obs_properties_add_bool(props, "fps_custom", obs_module_text("CustomFrameRate"));
obs_property_set_modified_callback(fps_set, is_fps_custom);
#ifndef ENABLE_BROWSER_SHARED_TEXTURE
obs_property_set_enabled(fps_set, false);
#endif
obs_properties_add_int(props, "fps", obs_module_text("FPS"), 1, 60, 1);
obs_property_t *p = obs_properties_add_text(props, "css", obs_module_text("CSS"), OBS_TEXT_MULTILINE);
obs_property_text_set_monospace(p, true);
obs_properties_add_bool(props, "shutdown", obs_module_text("ShutdownSourceNotVisible"));
obs_properties_add_bool(props, "restart_when_active", obs_module_text("RefreshBrowserActive"));
obs_property_t *controlLevel = obs_properties_add_list(props, "webpage_control_level",
obs_module_text("WebpageControlLevel"),
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
obs_property_list_add_int(controlLevel, obs_module_text("WebpageControlLevel.Level.None"),
(int)ControlLevel::None);
obs_property_list_add_int(controlLevel, obs_module_text("WebpageControlLevel.Level.ReadObs"),
(int)ControlLevel::ReadObs);
obs_property_list_add_int(controlLevel, obs_module_text("WebpageControlLevel.Level.ReadUser"),
(int)ControlLevel::ReadUser);
obs_property_list_add_int(controlLevel, obs_module_text("WebpageControlLevel.Level.Basic"),
(int)ControlLevel::Basic);
obs_property_list_add_int(controlLevel, obs_module_text("WebpageControlLevel.Level.Advanced"),
(int)ControlLevel::Advanced);
obs_property_list_add_int(controlLevel, obs_module_text("WebpageControlLevel.Level.All"),
(int)ControlLevel::All);
obs_properties_add_button2(
props, "refreshnocache", obs_module_text("RefreshNoCache"),
[](obs_properties_t *, obs_property_t *, void *data) {
static_cast<BrowserSource *>(data)->Refresh();
return false;
},
bs);
return props;
}
static void missing_file_callback(void *src, const char *new_path, void *data)
{
BrowserSource *bs = static_cast<BrowserSource *>(src);
if (bs) {
obs_source_t *source = bs->source;
OBSDataAutoRelease settings = obs_source_get_settings(source);
obs_data_set_string(settings, "local_file", new_path);
obs_source_update(source, settings);
}
UNUSED_PARAMETER(data);
}
static obs_missing_files_t *browser_source_missingfiles(void *data)
{
BrowserSource *bs = static_cast<BrowserSource *>(data);
obs_missing_files_t *files = obs_missing_files_create();
if (bs) {
obs_source_t *source = bs->source;
OBSDataAutoRelease settings = obs_source_get_settings(source);
bool enabled = obs_data_get_bool(settings, "is_local_file");
const char *path = obs_data_get_string(settings, "local_file");
if (enabled && strcmp(path, "") != 0) {
if (!os_file_exists(path)) {
obs_missing_file_t *file = obs_missing_file_create(
path, missing_file_callback, OBS_MISSING_FILE_SOURCE, bs->source, NULL);
obs_missing_files_add_file(files, file);
}
}
}
return files;
}
static CefRefPtr<BrowserApp> app;
static void BrowserInit(void)
{
string path = obs_get_module_binary_path(obs_current_module());
path = path.substr(0, path.find_last_of('/') + 1);
path += "//obs-browser-page";
#ifdef _WIN32
path += ".exe";
CefMainArgs args;
#else
/* On non-windows platforms, ie macOS, we'll want to pass thru flags to
* CEF */
struct obs_cmdline_args cmdline_args = obs_get_cmdline_args();
CefMainArgs args(cmdline_args.argc, cmdline_args.argv);
#endif
BPtr<char> conf_path = obs_module_config_path("");
os_mkdir(conf_path);
CefSettings settings;
settings.log_severity = LOGSEVERITY_FATAL;
BPtr<char> log_path = obs_module_config_path("debug.log");
BPtr<char> log_path_abs = os_get_abs_path_ptr(log_path);
CefString(&settings.log_file) = log_path_abs;
settings.windowless_rendering_enabled = true;
settings.no_sandbox = true;
uint32_t obs_ver = obs_get_version();
uint32_t obs_maj = obs_ver >> 24;
uint32_t obs_min = (obs_ver >> 16) & 0xFF;
uint32_t obs_pat = obs_ver & 0xFFFF;
/* This allows servers the ability to determine that browser panels and
* browser sources are coming from OBS. */
std::stringstream prod_ver;
prod_ver << "Chrome/";
prod_ver << std::to_string(cef_version_info(4)) << "." << std::to_string(cef_version_info(5)) << "."
<< std::to_string(cef_version_info(6)) << "." << std::to_string(cef_version_info(7));
prod_ver << " OBS/";
prod_ver << std::to_string(obs_maj) << "." << std::to_string(obs_min) << "." << std::to_string(obs_pat);
CefString(&settings.user_agent_product) = prod_ver.str();
#ifdef ENABLE_BROWSER_QT_LOOP
settings.external_message_pump = true;
settings.multi_threaded_message_loop = false;
#endif
#if !defined(_WIN32) && !defined(__APPLE__)
// Override locale path from OBS binary path to plugin binary path
string locales = obs_get_module_binary_path(obs_current_module());
locales = locales.substr(0, locales.find_last_of('/') + 1);
locales += "locales";
BPtr<char> abs_locales = os_get_abs_path_ptr(locales.c_str());
CefString(&settings.locales_dir_path) = abs_locales;
#endif
std::string obs_locale = obs_get_locale();
std::string accepted_languages;
if (obs_locale != "en-US") {
accepted_languages = obs_locale;
accepted_languages += ",";
accepted_languages += "en-US,en";
} else {
accepted_languages = "en-US,en";
}
BPtr<char> conf_path_abs = os_get_abs_path_ptr(conf_path);
CefString(&settings.locale) = obs_get_locale();
CefString(&settings.accept_language_list) = accepted_languages;
#if CHROME_VERSION_BUILD <= 6533
settings.persist_user_preferences = 1;
#endif
CefString(&settings.cache_path) = conf_path_abs;
#if !defined(__APPLE__) || defined(ENABLE_BROWSER_LEGACY)
char *abs_path = os_get_abs_path_ptr(path.c_str());
CefString(&settings.browser_subprocess_path) = abs_path;
bfree(abs_path);
#endif
bool tex_sharing_avail = false;
#ifdef ENABLE_BROWSER_SHARED_TEXTURE
if (hwaccel) {
obs_enter_graphics();
#if defined(__APPLE__) || defined(_WIN32)
hwaccel = tex_sharing_avail = gs_shared_texture_available();
#else
hwaccel = tex_sharing_avail = obs_cef_all_drm_formats_supported();
#endif
obs_leave_graphics();
}
#endif
#if defined(__APPLE__) || defined(_WIN32) || !defined(ENABLE_WAYLAND)
app = new BrowserApp(tex_sharing_avail);
#else
app = new BrowserApp(tex_sharing_avail, obs_get_nix_platform() == OBS_NIX_PLATFORM_WAYLAND);
#endif
#ifdef _WIN32
CefExecuteProcess(args, app, nullptr);
#endif
#if !defined(_WIN32)
BackupSignalHandlers();
bool success = CefInitialize(args, settings, app, nullptr);
RestoreSignalHandlers();
#else
bool success = CefInitialize(args, settings, app, nullptr);
#endif
if (!success) {
#if CHROME_VERSION_BUILD >= 6367
blog(LOG_ERROR, "[obs-browser]: CEF failed to initialize. Exit code: %d", CefGetExitCode());
#else
blog(LOG_ERROR, "[obs-browser]: CEF failed to initialize.");
#endif
return;
}
/* Register http://absolute/ scheme handler for older
* CEF builds which do not support file:// URLs */
CefRegisterSchemeHandlerFactory("http", "absolute", new BrowserSchemeHandlerFactory());
os_event_signal(cef_started_event);
}
static void BrowserShutdown(void)
{
CefClearSchemeHandlerFactories();
#ifdef ENABLE_BROWSER_QT_LOOP
while (messageObject.ExecuteNextBrowserTask())
;
CefDoMessageLoopWork();
#endif
CefShutdown();
app = nullptr;
}
#ifndef ENABLE_BROWSER_QT_LOOP
static void BrowserManagerThread(void)
{
BrowserInit();
CefRunMessageLoop();
BrowserShutdown();
}
#endif
extern "C" EXPORT void obs_browser_initialize(void)
{
if (!os_atomic_set_bool(&manager_initialized, true)) {
#ifdef ENABLE_BROWSER_QT_LOOP
BrowserInit();
#else
manager_thread = thread(BrowserManagerThread);
#endif
}
}
void RegisterBrowserSource()
{
struct obs_source_info info = {};
info.id = "browser_source";
info.type = OBS_SOURCE_TYPE_INPUT;
info.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_AUDIO | OBS_SOURCE_CUSTOM_DRAW | OBS_SOURCE_INTERACTION |
OBS_SOURCE_DO_NOT_DUPLICATE | OBS_SOURCE_SRGB;
info.get_properties = browser_source_get_properties;
info.get_defaults = browser_source_get_defaults;
info.icon_type = OBS_ICON_TYPE_BROWSER;
info.get_name = [](void *) {
return obs_module_text("BrowserSource");
};
info.create = [](obs_data_t *settings, obs_source_t *source) -> void * {
obs_browser_initialize();
return new BrowserSource(settings, source);
};
info.destroy = [](void *data) {
static_cast<BrowserSource *>(data)->Destroy();
};
info.missing_files = browser_source_missingfiles;
info.update = [](void *data, obs_data_t *settings) {
static_cast<BrowserSource *>(data)->Update(settings);
};
info.get_width = [](void *data) {
return (uint32_t)static_cast<BrowserSource *>(data)->width;
};
info.get_height = [](void *data) {
return (uint32_t)static_cast<BrowserSource *>(data)->height;
};
info.video_tick = [](void *data, float) {
static_cast<BrowserSource *>(data)->Tick();
};
info.video_render = [](void *data, gs_effect_t *) {
static_cast<BrowserSource *>(data)->Render();
};
info.mouse_click = [](void *data, const struct obs_mouse_event *event, int32_t type, bool mouse_up,
uint32_t click_count) {
static_cast<BrowserSource *>(data)->SendMouseClick(event, type, mouse_up, click_count);
};
info.mouse_move = [](void *data, const struct obs_mouse_event *event, bool mouse_leave) {
static_cast<BrowserSource *>(data)->SendMouseMove(event, mouse_leave);
};
info.mouse_wheel = [](void *data, const struct obs_mouse_event *event, int x_delta, int y_delta) {
static_cast<BrowserSource *>(data)->SendMouseWheel(event, x_delta, y_delta);
};
info.focus = [](void *data, bool focus) {
static_cast<BrowserSource *>(data)->SendFocus(focus);
};
info.key_click = [](void *data, const struct obs_key_event *event, bool key_up) {
static_cast<BrowserSource *>(data)->SendKeyClick(event, key_up);
};
info.show = [](void *data) {
static_cast<BrowserSource *>(data)->SetShowing(true);
};
info.hide = [](void *data) {
static_cast<BrowserSource *>(data)->SetShowing(false);
};
info.activate = [](void *data) {
BrowserSource *bs = static_cast<BrowserSource *>(data);
if (bs->restart)
bs->Refresh();
bs->SetActive(true);
};
info.deactivate = [](void *data) {
static_cast<BrowserSource *>(data)->SetActive(false);
};
obs_register_source(&info);
}
/* ========================================================================= */
extern void DispatchJSEvent(std::string eventName, std::string jsonString, BrowserSource *browser = nullptr);
static void handle_obs_frontend_event(enum obs_frontend_event event, void *)
{
switch (event) {
case OBS_FRONTEND_EVENT_STREAMING_STARTING:
DispatchJSEvent("obsStreamingStarting", "null");
break;
case OBS_FRONTEND_EVENT_STREAMING_STARTED:
DispatchJSEvent("obsStreamingStarted", "null");
break;
case OBS_FRONTEND_EVENT_STREAMING_STOPPING:
DispatchJSEvent("obsStreamingStopping", "null");
break;
case OBS_FRONTEND_EVENT_STREAMING_STOPPED:
DispatchJSEvent("obsStreamingStopped", "null");
break;
case OBS_FRONTEND_EVENT_RECORDING_STARTING:
DispatchJSEvent("obsRecordingStarting", "null");
break;
case OBS_FRONTEND_EVENT_RECORDING_STARTED:
DispatchJSEvent("obsRecordingStarted", "null");
break;
case OBS_FRONTEND_EVENT_RECORDING_PAUSED:
DispatchJSEvent("obsRecordingPaused", "null");
break;
case OBS_FRONTEND_EVENT_RECORDING_UNPAUSED:
DispatchJSEvent("obsRecordingUnpaused", "null");
break;
case OBS_FRONTEND_EVENT_RECORDING_STOPPING:
DispatchJSEvent("obsRecordingStopping", "null");
break;
case OBS_FRONTEND_EVENT_RECORDING_STOPPED:
DispatchJSEvent("obsRecordingStopped", "null");
break;
case OBS_FRONTEND_EVENT_REPLAY_BUFFER_STARTING:
DispatchJSEvent("obsReplaybufferStarting", "null");
break;
case OBS_FRONTEND_EVENT_REPLAY_BUFFER_STARTED:
DispatchJSEvent("obsReplaybufferStarted", "null");
break;
case OBS_FRONTEND_EVENT_REPLAY_BUFFER_SAVED:
DispatchJSEvent("obsReplaybufferSaved", "null");
break;
case OBS_FRONTEND_EVENT_REPLAY_BUFFER_STOPPING:
DispatchJSEvent("obsReplaybufferStopping", "null");
break;
case OBS_FRONTEND_EVENT_REPLAY_BUFFER_STOPPED:
DispatchJSEvent("obsReplaybufferStopped", "null");
break;
case OBS_FRONTEND_EVENT_VIRTUALCAM_STARTED:
DispatchJSEvent("obsVirtualcamStarted", "null");
break;
case OBS_FRONTEND_EVENT_VIRTUALCAM_STOPPED:
DispatchJSEvent("obsVirtualcamStopped", "null");
break;
case OBS_FRONTEND_EVENT_SCENE_CHANGED: {
OBSSourceAutoRelease source = obs_frontend_get_current_scene();
if (!source)
break;
const char *name = obs_source_get_name(source);
if (!name)
break;
nlohmann::json json = {{"name", name},
{"width", obs_source_get_width(source)},
{"height", obs_source_get_height(source)}};
DispatchJSEvent("obsSceneChanged", json.dump());
break;
}
case OBS_FRONTEND_EVENT_SCENE_LIST_CHANGED: {
struct obs_frontend_source_list list = {};
obs_frontend_get_scenes(&list);
std::vector<const char *> scenes_vector;
for (size_t i = 0; i < list.sources.num; i++) {
obs_source_t *source = list.sources.array[i];
scenes_vector.push_back(obs_source_get_name(source));
}
nlohmann::json json = scenes_vector;
obs_frontend_source_list_free(&list);
DispatchJSEvent("obsSceneListChanged", json.dump());
break;
}
case OBS_FRONTEND_EVENT_TRANSITION_CHANGED: {
OBSSourceAutoRelease source = obs_frontend_get_current_transition();
if (!source)
break;
const char *name = obs_source_get_name(source);
if (!name)
break;
nlohmann::json json = {{"name", name}};
DispatchJSEvent("obsTransitionChanged", json.dump());
break;
}
case OBS_FRONTEND_EVENT_TRANSITION_LIST_CHANGED: {
struct obs_frontend_source_list list = {};
obs_frontend_get_transitions(&list);
std::vector<const char *> transitions_vector;
for (size_t i = 0; i < list.sources.num; i++) {
obs_source_t *source = list.sources.array[i];
transitions_vector.push_back(obs_source_get_name(source));
}
nlohmann::json json = transitions_vector;
obs_frontend_source_list_free(&list);
DispatchJSEvent("obsTransitionListChanged", json.dump());
break;
}
case OBS_FRONTEND_EVENT_EXIT:
DispatchJSEvent("obsExit", "null");
break;
default:;
}
}
#ifdef _WIN32
static inline void EnumAdapterCount()
{
ComPtr<IDXGIFactory1> factory;
ComPtr<IDXGIAdapter1> adapter;
HRESULT hr;
UINT i = 0;
hr = CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void **)&factory);
if (FAILED(hr))
return;
while (factory->EnumAdapters1(i++, &adapter) == S_OK) {
DXGI_ADAPTER_DESC desc;
hr = adapter->GetDesc(&desc);
if (FAILED(hr))
continue;
if (i == 1)
deviceId = desc.Description;
/* ignore Microsoft's 'basic' renderer' */
if (desc.VendorId == 0x1414 && desc.DeviceId == 0x8c)
continue;
adapterCount++;
}
}
#endif
#ifdef ENABLE_BROWSER_SHARED_TEXTURE
#ifdef _WIN32
static const wchar_t *blacklisted_devices[] = {L"Intel", L"Microsoft", L"Radeon HD 8850M", L"Radeon HD 7660", nullptr};
static inline bool is_intel(const std::wstring &str)
{
return wstrstri(str.c_str(), L"Intel") != 0;
}
static void check_hwaccel_support(void)
{
/* do not use hardware acceleration if a blacklisted device is the
* default and on 2 or more adapters */
const wchar_t **device = blacklisted_devices;
if (adapterCount >= 2 || !is_intel(deviceId)) {
while (*device) {
if (!!wstrstri(deviceId.c_str(), *device)) {
hwaccel = false;
blog(LOG_INFO, "[obs-browser]: "
"Blacklisted device "
"detected, "
"disabling browser "
"source hardware "
"acceleration.");
break;
}
device++;
}
}
}
#elif __linux__
static void check_hwaccel_support(void)
{
/* NOTE: GL_VERSION returns a string that contains the driver vendor */
const char *glVersion = NULL;
obs_enter_graphics();
glVersion = gs_get_driver_version();
obs_leave_graphics();
if (!glVersion)
return;
if (strstr(glVersion, "NVIDIA") != NULL) {
hwaccel = false;
blog(LOG_INFO,
"[obs-browser]: Blacklisted driver detected, disabling browser source hardware acceleration.");
}
return;
}
#else
static void check_hwaccel_support(void)
{
return;
}
#endif
#endif
bool obs_module_load(void)
{
#ifdef ENABLE_BROWSER_QT_LOOP
qRegisterMetaType<MessageTask>("MessageTask");
#endif
os_event_init(&cef_started_event, OS_EVENT_TYPE_MANUAL);
#if defined(_WIN32) && CHROME_VERSION_BUILD < 5615
/* CefEnableHighDPISupport doesn't do anything on OS other than Windows. Would also crash macOS at this point as CEF is not directly linked */
CefEnableHighDPISupport();
#endif
#ifdef _WIN32
EnumAdapterCount();
#else
#if defined(__APPLE__) && !defined(ENABLE_BROWSER_LEGACY)
/* Load CEF at runtime as required on macOS */
CefScopedLibraryLoader library_loader;
if (!library_loader.LoadInMain())
return false;
#endif
#endif
blog(LOG_INFO, "[obs-browser]: Version %s", OBS_BROWSER_VERSION_STRING);
blog(LOG_INFO, "[obs-browser]: CEF Version %i.%i.%i.%i (runtime), %s (compiled)", cef_version_info(4),
cef_version_info(5), cef_version_info(6), cef_version_info(7), CEF_VERSION);
RegisterBrowserSource();
obs_frontend_add_event_callback(handle_obs_frontend_event, nullptr);
#ifdef ENABLE_BROWSER_SHARED_TEXTURE
OBSDataAutoRelease private_data = obs_get_private_data();
hwaccel = obs_data_get_bool(private_data, "BrowserHWAccel");
if (hwaccel) {
check_hwaccel_support();
}
#endif
return true;
}
void obs_module_post_load(void)
{
auto vendor = obs_websocket_register_vendor("obs-browser");
if (!vendor)
return;
auto emit_event_request_cb = [](obs_data_t *request_data, obs_data_t *, void *) {
const char *event_name = obs_data_get_string(request_data, "event_name");
if (!event_name)
return;
OBSDataAutoRelease event_data = obs_data_get_obj(request_data, "event_data");
const char *event_data_string = event_data ? obs_data_get_json(event_data) : "{}";
DispatchJSEvent(event_name, event_data_string, nullptr);
};
if (!obs_websocket_vendor_register_request(vendor, "emit_event", emit_event_request_cb, nullptr))
blog(LOG_WARNING, "[obs-browser]: Failed to register obs-websocket request emit_event");
}
void obs_module_unload(void)
{
#ifdef ENABLE_BROWSER_QT_LOOP
BrowserShutdown();
#else
if (manager_thread.joinable()) {
if (!QueueCEFTask([]() { CefQuitMessageLoop(); }))
blog(LOG_DEBUG, "[obs-browser]: Failed to post CefQuit task to loop");
manager_thread.join();
}
#endif
os_event_destroy(cef_started_event);
}
0707010000008A000081A400000000000000000000000168D5836B000045DE000000000000000000000000000000000000004C00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/obs-browser-source.cpp/******************************************************************************
Copyright (C) 2014 by John R. Bradley <jrb@turrettech.com>
Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
#include "obs-browser-source.hpp"
#include "browser-client.hpp"
#include "browser-scheme.hpp"
#include "wide-string.hpp"
#include <nlohmann/json.hpp>
#include <util/threading.h>
#include <QApplication>
#include <util/dstr.h>
#include <functional>
#include <thread>
#include <mutex>
#ifdef __linux__
#include "linux-keyboard-helpers.hpp"
#endif
#ifdef ENABLE_BROWSER_QT_LOOP
#include <QEventLoop>
#include <QThread>
#endif
#if !defined(_WIN32) && !defined(__APPLE__)
#include "drm-format.hpp"
#endif
using namespace std;
extern bool QueueCEFTask(std::function<void()> task);
static mutex browser_list_mutex;
static BrowserSource *first_browser = nullptr;
static void SendBrowserVisibility(CefRefPtr<CefBrowser> browser, bool isVisible)
{
if (!browser)
return;
if (isVisible) {
browser->GetHost()->WasResized();
browser->GetHost()->WasHidden(false);
browser->GetHost()->Invalidate(PET_VIEW);
} else {
browser->GetHost()->WasHidden(true);
}
CefRefPtr<CefProcessMessage> msg = CefProcessMessage::Create("Visibility");
CefRefPtr<CefListValue> args = msg->GetArgumentList();
args->SetBool(0, isVisible);
SendBrowserProcessMessage(browser, PID_RENDERER, msg);
}
void DispatchJSEvent(std::string eventName, std::string jsonString, BrowserSource *browser = nullptr);
BrowserSource::BrowserSource(obs_data_t *, obs_source_t *source_) : source(source_)
{
/* Register Refresh hotkey */
auto refreshFunction = [](void *data, obs_hotkey_id, obs_hotkey_t *, bool pressed) {
if (pressed) {
BrowserSource *bs = (BrowserSource *)data;
bs->Refresh();
}
};
obs_hotkey_register_source(source, "ObsBrowser.Refresh", obs_module_text("RefreshNoCache"), refreshFunction,
(void *)this);
auto jsEventFunction = [](void *p, calldata_t *calldata) {
const auto eventName = calldata_string(calldata, "eventName");
if (!eventName)
return;
auto jsonString = calldata_string(calldata, "jsonString");
if (!jsonString)
jsonString = "null";
DispatchJSEvent(eventName, jsonString, (BrowserSource *)p);
};
proc_handler_t *ph = obs_source_get_proc_handler(source);
proc_handler_add(ph, "void javascript_event(string eventName, string jsonString)", jsEventFunction,
(void *)this);
/* defer update */
obs_source_update(source, nullptr);
lock_guard<mutex> lock(browser_list_mutex);
p_prev_next = &first_browser;
next = first_browser;
if (first_browser)
first_browser->p_prev_next = &next;
first_browser = this;
}
static void ActuallyCloseBrowser(CefRefPtr<CefBrowser> cefBrowser)
{
CefRefPtr<CefClient> client = cefBrowser->GetHost()->GetClient();
BrowserClient *bc = reinterpret_cast<BrowserClient *>(client.get());
if (bc) {
bc->bs = nullptr;
}
/*
* This stops rendering
* http://magpcss.org/ceforum/viewtopic.php?f=6&t=12079
* https://bitbucket.org/chromiumembedded/cef/issues/1363/washidden-api-got-broken-on-branch-2062)
*/
cefBrowser->GetHost()->WasHidden(true);
cefBrowser->GetHost()->CloseBrowser(true);
}
BrowserSource::~BrowserSource()
{
if (cefBrowser)
ActuallyCloseBrowser(cefBrowser);
}
void BrowserSource::Destroy()
{
destroying = true;
DestroyTextures();
lock_guard<mutex> lock(browser_list_mutex);
if (next)
next->p_prev_next = p_prev_next;
*p_prev_next = next;
QueueCEFTask([this]() { delete this; });
}
void BrowserSource::ExecuteOnBrowser(BrowserFunc func, bool async)
{
if (!async) {
#ifdef ENABLE_BROWSER_QT_LOOP
if (QThread::currentThread() == qApp->thread()) {
if (!!cefBrowser)
func(cefBrowser);
return;
}
#endif
os_event_t *finishedEvent;
os_event_init(&finishedEvent, OS_EVENT_TYPE_AUTO);
bool success = QueueCEFTask([&]() {
if (!!cefBrowser)
func(cefBrowser);
os_event_signal(finishedEvent);
});
if (success) {
os_event_wait(finishedEvent);
}
os_event_destroy(finishedEvent);
} else {
CefRefPtr<CefBrowser> browser = GetBrowser();
if (!!browser) {
#ifdef ENABLE_BROWSER_QT_LOOP
QueueBrowserTask(cefBrowser, func);
#else
QueueCEFTask([=]() { func(browser); });
#endif
}
}
}
bool BrowserSource::CreateBrowser()
{
return QueueCEFTask([this]() {
#ifdef ENABLE_BROWSER_SHARED_TEXTURE
if (hwaccel) {
obs_enter_graphics();
#if defined(__APPLE__) || defined(_WIN32)
tex_sharing_avail = gs_shared_texture_available();
#else
tex_sharing_avail = obs_cef_all_drm_formats_supported();
#endif
obs_leave_graphics();
}
#else
bool hwaccel = false;
#endif
CefRefPtr<BrowserClient> browserClient =
new BrowserClient(this, hwaccel && tex_sharing_avail, reroute_audio, webpage_control_level);
CefWindowInfo windowInfo;
windowInfo.bounds.width = width;
windowInfo.bounds.height = height;
windowInfo.windowless_rendering_enabled = true;
#ifdef ENABLE_BROWSER_SHARED_TEXTURE
windowInfo.shared_texture_enabled = hwaccel;
#endif
CefBrowserSettings cefBrowserSettings;
#ifdef ENABLE_BROWSER_SHARED_TEXTURE
#ifdef BROWSER_EXTERNAL_BEGIN_FRAME_ENABLED
if (!fps_custom) {
windowInfo.external_begin_frame_enabled = true;
cefBrowserSettings.windowless_frame_rate = 0;
} else {
cefBrowserSettings.windowless_frame_rate = fps;
}
#else
struct obs_video_info ovi;
obs_get_video_info(&ovi);
canvas_fps = (double)ovi.fps_num / (double)ovi.fps_den;
cefBrowserSettings.windowless_frame_rate = (fps_custom) ? fps : canvas_fps;
#endif
#else
cefBrowserSettings.windowless_frame_rate = fps;
#endif
cefBrowserSettings.default_font_size = 16;
cefBrowserSettings.default_fixed_font_size = 16;
auto browser = CefBrowserHost::CreateBrowserSync(windowInfo, browserClient, url, cefBrowserSettings,
CefRefPtr<CefDictionaryValue>(), nullptr);
SetBrowser(browser);
if (reroute_audio)
cefBrowser->GetHost()->SetAudioMuted(true);
if (obs_source_showing(source))
is_showing = true;
SendBrowserVisibility(cefBrowser, is_showing);
});
}
void BrowserSource::DestroyBrowser()
{
ExecuteOnBrowser(ActuallyCloseBrowser, true);
SetBrowser(nullptr);
}
void BrowserSource::SendMouseClick(const struct obs_mouse_event *event, int32_t type, bool mouse_up,
uint32_t click_count)
{
uint32_t modifiers = event->modifiers;
int32_t x = event->x;
int32_t y = event->y;
ExecuteOnBrowser(
[=](CefRefPtr<CefBrowser> cefBrowser) {
CefMouseEvent e;
e.modifiers = modifiers;
e.x = x;
e.y = y;
CefBrowserHost::MouseButtonType buttonType = (CefBrowserHost::MouseButtonType)type;
cefBrowser->GetHost()->SendMouseClickEvent(e, buttonType, mouse_up, click_count);
},
true);
}
void BrowserSource::SendMouseMove(const struct obs_mouse_event *event, bool mouse_leave)
{
uint32_t modifiers = event->modifiers;
int32_t x = event->x;
int32_t y = event->y;
ExecuteOnBrowser(
[=](CefRefPtr<CefBrowser> cefBrowser) {
CefMouseEvent e;
e.modifiers = modifiers;
e.x = x;
e.y = y;
cefBrowser->GetHost()->SendMouseMoveEvent(e, mouse_leave);
},
true);
}
void BrowserSource::SendMouseWheel(const struct obs_mouse_event *event, int x_delta, int y_delta)
{
uint32_t modifiers = event->modifiers;
int32_t x = event->x;
int32_t y = event->y;
ExecuteOnBrowser(
[=](CefRefPtr<CefBrowser> cefBrowser) {
CefMouseEvent e;
e.modifiers = modifiers;
e.x = x;
e.y = y;
cefBrowser->GetHost()->SendMouseWheelEvent(e, x_delta, y_delta);
},
true);
}
void BrowserSource::SendFocus(bool focus)
{
ExecuteOnBrowser([=](CefRefPtr<CefBrowser> cefBrowser) { cefBrowser->GetHost()->SetFocus(focus); }, true);
}
void BrowserSource::SendKeyClick(const struct obs_key_event *event, bool key_up)
{
if (destroying)
return;
std::string text = event->text;
#ifdef __linux__
uint32_t native_vkey = KeyboardCodeFromXKeysym(event->native_vkey);
uint32_t modifiers = event->native_modifiers;
#elif defined(_WIN32) || defined(__APPLE__)
uint32_t native_vkey = event->native_vkey;
uint32_t modifiers = event->modifiers;
#else
uint32_t native_vkey = event->native_vkey;
uint32_t native_scancode = event->native_scancode;
uint32_t modifiers = event->native_modifiers;
#endif
ExecuteOnBrowser(
[=](CefRefPtr<CefBrowser> cefBrowser) {
CefKeyEvent e;
e.windows_key_code = native_vkey;
#ifdef __APPLE__
e.native_key_code = native_vkey;
#endif
e.type = key_up ? KEYEVENT_KEYUP : KEYEVENT_RAWKEYDOWN;
if (!text.empty()) {
wstring wide = to_wide(text);
if (wide.size())
e.character = wide[0];
}
//e.native_key_code = native_vkey;
e.modifiers = modifiers;
cefBrowser->GetHost()->SendKeyEvent(e);
if (!text.empty() && !key_up) {
e.type = KEYEVENT_CHAR;
#ifdef __linux__
e.windows_key_code = KeyboardCodeFromXKeysym(e.character);
#elif defined(_WIN32)
e.windows_key_code = e.character;
#elif !defined(__APPLE__)
e.native_key_code = native_scancode;
#endif
cefBrowser->GetHost()->SendKeyEvent(e);
}
},
true);
}
void BrowserSource::SetShowing(bool showing)
{
if (destroying)
return;
is_showing = showing;
if (shutdown_on_invisible) {
if (showing) {
Update();
} else {
DestroyBrowser();
}
} else {
ExecuteOnBrowser(
[=](CefRefPtr<CefBrowser> cefBrowser) {
CefRefPtr<CefProcessMessage> msg = CefProcessMessage::Create("Visibility");
CefRefPtr<CefListValue> args = msg->GetArgumentList();
args->SetBool(0, showing);
SendBrowserProcessMessage(cefBrowser, PID_RENDERER, msg);
},
true);
nlohmann::json json;
json["visible"] = showing;
DispatchJSEvent("obsSourceVisibleChanged", json.dump(), this);
#if defined(BROWSER_EXTERNAL_BEGIN_FRAME_ENABLED) && defined(ENABLE_BROWSER_SHARED_TEXTURE)
if (showing && !fps_custom) {
reset_frame = false;
}
#endif
SendBrowserVisibility(cefBrowser, showing);
if (showing)
return;
obs_enter_graphics();
if (!hwaccel && texture) {
DestroyTextures();
}
obs_leave_graphics();
}
}
void BrowserSource::SetActive(bool active)
{
ExecuteOnBrowser(
[=](CefRefPtr<CefBrowser> cefBrowser) {
CefRefPtr<CefProcessMessage> msg = CefProcessMessage::Create("Active");
CefRefPtr<CefListValue> args = msg->GetArgumentList();
args->SetBool(0, active);
SendBrowserProcessMessage(cefBrowser, PID_RENDERER, msg);
},
true);
nlohmann::json json;
json["active"] = active;
DispatchJSEvent("obsSourceActiveChanged", json.dump(), this);
}
void BrowserSource::Refresh()
{
ExecuteOnBrowser([](CefRefPtr<CefBrowser> cefBrowser) { cefBrowser->ReloadIgnoreCache(); }, true);
}
void BrowserSource::SetBrowser(CefRefPtr<CefBrowser> b)
{
std::lock_guard<std::recursive_mutex> auto_lock(lockBrowser);
cefBrowser = b;
}
CefRefPtr<CefBrowser> BrowserSource::GetBrowser()
{
std::lock_guard<std::recursive_mutex> auto_lock(lockBrowser);
return cefBrowser;
}
#ifdef ENABLE_BROWSER_SHARED_TEXTURE
#ifdef BROWSER_EXTERNAL_BEGIN_FRAME_ENABLED
inline void BrowserSource::SignalBeginFrame()
{
if (reset_frame) {
ExecuteOnBrowser(
[](CefRefPtr<CefBrowser> cefBrowser) { cefBrowser->GetHost()->SendExternalBeginFrame(); },
true);
reset_frame = false;
}
}
#endif
#endif
void BrowserSource::Update(obs_data_t *settings)
{
if (settings) {
bool n_is_local;
int n_width;
int n_height;
bool n_fps_custom;
int n_fps;
bool n_shutdown;
bool n_restart;
bool n_reroute;
ControlLevel n_webpage_control_level;
std::string n_url;
std::string n_css;
n_is_local = obs_data_get_bool(settings, "is_local_file");
n_width = (int)obs_data_get_int(settings, "width");
n_height = (int)obs_data_get_int(settings, "height");
n_fps_custom = obs_data_get_bool(settings, "fps_custom");
n_fps = (int)obs_data_get_int(settings, "fps");
n_shutdown = obs_data_get_bool(settings, "shutdown");
n_restart = obs_data_get_bool(settings, "restart_when_active");
n_css = obs_data_get_string(settings, "css");
n_url = obs_data_get_string(settings, n_is_local ? "local_file" : "url");
n_reroute = obs_data_get_bool(settings, "reroute_audio");
n_webpage_control_level =
static_cast<ControlLevel>(obs_data_get_int(settings, "webpage_control_level"));
if (n_is_local && !n_url.empty()) {
n_url = CefURIEncode(n_url, false);
#ifdef _WIN32
size_t slash = n_url.find("%2F");
size_t colon = n_url.find("%3A");
if (slash != std::string::npos && colon != std::string::npos && colon < slash)
n_url.replace(colon, 3, ":");
#endif
while (n_url.find("%5C") != std::string::npos)
n_url.replace(n_url.find("%5C"), 3, "/");
while (n_url.find("%2F") != std::string::npos)
n_url.replace(n_url.find("%2F"), 3, "/");
/* http://absolute/ based mapping for older CEF */
n_url = "http://absolute/" + n_url;
}
if (n_is_local == is_local && n_fps_custom == fps_custom && n_fps == fps &&
n_shutdown == shutdown_on_invisible && n_restart == restart && n_css == css && n_url == url &&
n_reroute == reroute_audio && n_webpage_control_level == webpage_control_level) {
if (n_width == width && n_height == height)
return;
width = n_width;
height = n_height;
ExecuteOnBrowser(
[=](CefRefPtr<CefBrowser> cefBrowser) {
const CefSize cefSize(width, height);
cefBrowser->GetHost()->GetClient()->GetDisplayHandler()->OnAutoResize(
cefBrowser, cefSize);
cefBrowser->GetHost()->WasResized();
cefBrowser->GetHost()->Invalidate(PET_VIEW);
},
true);
return;
}
is_local = n_is_local;
width = n_width;
height = n_height;
fps = n_fps;
fps_custom = n_fps_custom;
shutdown_on_invisible = n_shutdown;
reroute_audio = n_reroute;
webpage_control_level = n_webpage_control_level;
restart = n_restart;
css = n_css;
url = n_url;
obs_source_set_audio_active(source, reroute_audio);
}
DestroyBrowser();
DestroyTextures();
if (!shutdown_on_invisible || obs_source_showing(source))
create_browser = true;
first_update = false;
}
void BrowserSource::Tick()
{
if (create_browser && CreateBrowser())
create_browser = false;
#if defined(ENABLE_BROWSER_SHARED_TEXTURE)
#if defined(BROWSER_EXTERNAL_BEGIN_FRAME_ENABLED)
if (!fps_custom)
reset_frame = true;
#else
struct obs_video_info ovi;
obs_get_video_info(&ovi);
double video_fps = (double)ovi.fps_num / (double)ovi.fps_den;
if (!fps_custom) {
if (!!cefBrowser && canvas_fps != video_fps) {
cefBrowser->GetHost()->SetWindowlessFrameRate(video_fps);
canvas_fps = video_fps;
}
}
#endif
#endif
}
extern void ProcessCef();
void BrowserSource::Render()
{
bool flip = false;
#if defined(ENABLE_BROWSER_SHARED_TEXTURE) && CHROME_VERSION_BUILD < 6367
flip = hwaccel;
#endif
if (texture) {
#ifdef __APPLE__
int type = gs_get_device_type();
gs_effect_t *effect;
if (type == GS_DEVICE_OPENGL) {
effect = obs_get_base_effect((hwaccel) ? OBS_EFFECT_DEFAULT_RECT : OBS_EFFECT_DEFAULT);
} else {
effect = obs_get_base_effect(OBS_EFFECT_DEFAULT);
}
#else
gs_effect_t *effect = obs_get_base_effect(OBS_EFFECT_DEFAULT);
#endif
bool linear_sample = extra_texture == NULL;
gs_texture_t *draw_texture = texture;
if (!linear_sample && !obs_source_get_texcoords_centered(source)) {
gs_copy_texture(extra_texture, texture);
draw_texture = extra_texture;
linear_sample = true;
}
const bool previous = gs_framebuffer_srgb_enabled();
gs_enable_framebuffer_srgb(true);
gs_blend_state_push();
gs_blend_function(GS_BLEND_ONE, GS_BLEND_INVSRCALPHA);
gs_eparam_t *const image = gs_effect_get_param_by_name(effect, "image");
const char *tech;
if (linear_sample) {
gs_effect_set_texture_srgb(image, draw_texture);
tech = "Draw";
} else {
gs_effect_set_texture(image, draw_texture);
tech = "DrawSrgbDecompress";
}
const uint32_t flip_flag = flip ? GS_FLIP_V : 0;
while (gs_effect_loop(effect, tech))
gs_draw_sprite(draw_texture, flip_flag, 0, 0);
gs_blend_state_pop();
gs_enable_framebuffer_srgb(previous);
}
#if defined(BROWSER_EXTERNAL_BEGIN_FRAME_ENABLED) && defined(ENABLE_BROWSER_SHARED_TEXTURE)
SignalBeginFrame();
#elif defined(ENABLE_BROWSER_QT_LOOP)
ProcessCef();
#endif
}
static void ExecuteOnBrowser(BrowserFunc func, BrowserSource *bs)
{
lock_guard<mutex> lock(browser_list_mutex);
if (bs) {
BrowserSource *bsw = reinterpret_cast<BrowserSource *>(bs);
bsw->ExecuteOnBrowser(func, true);
}
}
static void ExecuteOnAllBrowsers(BrowserFunc func)
{
lock_guard<mutex> lock(browser_list_mutex);
BrowserSource *bs = first_browser;
while (bs) {
BrowserSource *bsw = reinterpret_cast<BrowserSource *>(bs);
bsw->ExecuteOnBrowser(func, true);
bs = bs->next;
}
}
void DispatchJSEvent(std::string eventName, std::string jsonString, BrowserSource *browser)
{
const auto jsEvent = [=](CefRefPtr<CefBrowser> cefBrowser) {
CefRefPtr<CefProcessMessage> msg = CefProcessMessage::Create("DispatchJSEvent");
CefRefPtr<CefListValue> args = msg->GetArgumentList();
args->SetString(0, eventName);
args->SetString(1, jsonString);
SendBrowserProcessMessage(cefBrowser, PID_RENDERER, msg);
};
if (!browser)
ExecuteOnAllBrowsers(jsEvent);
else
ExecuteOnBrowser(jsEvent, browser);
}
0707010000008B000081A400000000000000000000000168D5836B00000E82000000000000000000000000000000000000004C00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/obs-browser-source.hpp/******************************************************************************
Copyright (C) 2014 by John R. Bradley <jrb@turrettech.com>
Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
#pragma once
#include <obs-module.h>
#include "cef-headers.hpp"
#include "browser-app.hpp"
#include <atomic>
#include <functional>
#include <string>
#include <mutex>
enum class ControlLevel : int {
None,
ReadObs,
ReadUser,
Basic,
Advanced,
All,
};
inline constexpr ControlLevel DEFAULT_CONTROL_LEVEL = ControlLevel::ReadObs;
extern bool hwaccel;
struct BrowserSource {
BrowserSource **p_prev_next = nullptr;
BrowserSource *next = nullptr;
obs_source_t *source = nullptr;
bool tex_sharing_avail = false;
bool create_browser = false;
std::recursive_mutex lockBrowser;
CefRefPtr<CefBrowser> cefBrowser;
std::string url;
std::string css;
gs_texture_t *texture = nullptr;
gs_texture_t *extra_texture = nullptr;
uint32_t last_cx = 0;
uint32_t last_cy = 0;
gs_color_format last_format = GS_UNKNOWN;
#ifdef ENABLE_BROWSER_SHARED_TEXTURE
#ifdef _WIN32
void *last_handle = INVALID_HANDLE_VALUE;
#elif defined(__APPLE__)
void *last_handle = nullptr;
#endif
#endif
int width = 0;
int height = 0;
bool fps_custom = false;
int fps = 0;
double canvas_fps = 0;
bool restart = false;
bool shutdown_on_invisible = false;
bool is_local = false;
bool first_update = true;
bool reroute_audio = true;
std::atomic<bool> destroying = false;
ControlLevel webpage_control_level = DEFAULT_CONTROL_LEVEL;
#if defined(BROWSER_EXTERNAL_BEGIN_FRAME_ENABLED) && defined(ENABLE_BROWSER_SHARED_TEXTURE)
bool reset_frame = false;
#endif
bool is_showing = false;
inline void DestroyTextures()
{
obs_enter_graphics();
if (extra_texture) {
gs_texture_destroy(extra_texture);
extra_texture = nullptr;
last_cx = 0;
last_cy = 0;
last_format = GS_UNKNOWN;
}
if (texture) {
gs_texture_destroy(texture);
texture = nullptr;
}
obs_leave_graphics();
}
/* ---------------------------- */
bool CreateBrowser();
void DestroyBrowser();
void ExecuteOnBrowser(BrowserFunc func, bool async = false);
/* ---------------------------- */
BrowserSource(obs_data_t *settings, obs_source_t *source);
~BrowserSource();
void Destroy();
void Update(obs_data_t *settings = nullptr);
void Tick();
void Render();
void SendMouseClick(const struct obs_mouse_event *event, int32_t type, bool mouse_up, uint32_t click_count);
void SendMouseMove(const struct obs_mouse_event *event, bool mouse_leave);
void SendMouseWheel(const struct obs_mouse_event *event, int x_delta, int y_delta);
void SendFocus(bool focus);
void SendKeyClick(const struct obs_key_event *event, bool key_up);
void SetShowing(bool showing);
void SetActive(bool active);
void Refresh();
#if defined(BROWSER_EXTERNAL_BEGIN_FRAME_ENABLED) && defined(ENABLE_BROWSER_SHARED_TEXTURE)
inline void SignalBeginFrame();
#endif
void SetBrowser(CefRefPtr<CefBrowser> b);
CefRefPtr<CefBrowser> GetBrowser();
};
0707010000008C000041ED00000000000000000000000268D5836B00000000000000000000000000000000000000000000003B00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/panel0707010000008D000081A400000000000000000000000168D5836B00003D49000000000000000000000000000000000000005400000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/panel/browser-panel-client.cpp#include "browser-panel-client.hpp"
#include <util/dstr.h>
#include <QUrl>
#include <QDesktopServices>
#include <QApplication>
#include <QMenu>
#include <QThread>
#include <QMessageBox>
#include <QInputDialog>
#include <QRegularExpression>
#include <QLabel>
#include <QClipboard>
#include <obs-module.h>
#ifdef _WIN32
#include <windows.h>
#endif
#if !defined(_WIN32) && !defined(__APPLE__)
#include <X11/Xlib.h>
#endif
#define MENU_ITEM_DEVTOOLS MENU_ID_CUSTOM_FIRST
#define MENU_ITEM_MUTE MENU_ID_CUSTOM_FIRST + 1
#define MENU_ITEM_ZOOM_IN MENU_ID_CUSTOM_FIRST + 2
#define MENU_ITEM_ZOOM_RESET MENU_ID_CUSTOM_FIRST + 3
#define MENU_ITEM_ZOOM_OUT MENU_ID_CUSTOM_FIRST + 4
#define MENU_ITEM_COPY_URL MENU_ID_CUSTOM_FIRST + 5
/* CefClient */
CefRefPtr<CefLoadHandler> QCefBrowserClient::GetLoadHandler()
{
return this;
}
CefRefPtr<CefDisplayHandler> QCefBrowserClient::GetDisplayHandler()
{
return this;
}
CefRefPtr<CefRequestHandler> QCefBrowserClient::GetRequestHandler()
{
return this;
}
CefRefPtr<CefLifeSpanHandler> QCefBrowserClient::GetLifeSpanHandler()
{
return this;
}
CefRefPtr<CefFocusHandler> QCefBrowserClient::GetFocusHandler()
{
return this;
}
CefRefPtr<CefContextMenuHandler> QCefBrowserClient::GetContextMenuHandler()
{
return this;
}
CefRefPtr<CefKeyboardHandler> QCefBrowserClient::GetKeyboardHandler()
{
return this;
}
CefRefPtr<CefJSDialogHandler> QCefBrowserClient::GetJSDialogHandler()
{
return this;
}
/* CefDisplayHandler */
void QCefBrowserClient::OnTitleChange(CefRefPtr<CefBrowser> browser, const CefString &title)
{
if (widget && widget->cefBrowser->IsSame(browser)) {
std::string str_title = title;
QString qt_title = QString::fromUtf8(str_title.c_str());
QMetaObject::invokeMethod(widget, "titleChanged", Q_ARG(QString, qt_title));
} else { /* handle popup title */
CefString newTitle = title;
if (title.compare("DevTools") == 0 && widget)
newTitle = QString(obs_module_text("DevTools"))
.arg(widget->parentWidget()->windowTitle())
.toUtf8()
.constData();
#if defined(_WIN32)
CefWindowHandle handl = browser->GetHost()->GetWindowHandle();
std::wstring str_title = newTitle;
SetWindowTextW((HWND)handl, str_title.c_str());
#elif defined(__linux__)
CefWindowHandle handl = browser->GetHost()->GetWindowHandle();
XStoreName(cef_get_xdisplay(), handl, newTitle.ToString().c_str());
#endif
}
}
/* CefRequestHandler */
bool QCefBrowserClient::OnBeforeBrowse(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame>,
CefRefPtr<CefRequest> request, bool, bool)
{
std::string str_url = request->GetURL();
std::lock_guard<std::mutex> lock(popup_whitelist_mutex);
for (size_t i = forced_popups.size(); i > 0; i--) {
PopupWhitelistInfo &info = forced_popups[i - 1];
if (!info.obj) {
forced_popups.erase(forced_popups.begin() + (i - 1));
continue;
}
if (astrcmpi(info.url.c_str(), str_url.c_str()) == 0) {
/* Open tab popup URLs in user's actual browser */
QUrl url = QUrl(str_url.c_str(), QUrl::TolerantMode);
QDesktopServices::openUrl(url);
browser->GoBack();
return true;
}
}
if (widget) {
QString qt_url = QString::fromUtf8(str_url.c_str());
QMetaObject::invokeMethod(widget, "urlChanged", Q_ARG(QString, qt_url));
}
return false;
}
bool QCefBrowserClient::OnOpenURLFromTab(CefRefPtr<CefBrowser>, CefRefPtr<CefFrame>, const CefString &target_url,
CefRequestHandler::WindowOpenDisposition, bool)
{
std::string str_url = target_url;
/* Open tab popup URLs in user's actual browser */
QUrl url = QUrl(str_url.c_str(), QUrl::TolerantMode);
QDesktopServices::openUrl(url);
return true;
}
void QCefBrowserClient::OnLoadError(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame,
CefLoadHandler::ErrorCode errorCode, const CefString &errorText,
const CefString &failedUrl)
{
UNUSED_PARAMETER(browser);
if (errorCode == ERR_ABORTED)
return;
struct dstr html;
char *path = obs_module_file("error.html");
char *errorPage = os_quick_read_utf8_file(path);
dstr_init_copy(&html, errorPage);
dstr_replace(&html, "%%ERROR_URL%%", failedUrl.ToString().c_str());
dstr_replace(&html, "Error.Title", obs_module_text("Error.Title"));
dstr_replace(&html, "Error.Description", obs_module_text("Error.Description"));
dstr_replace(&html, "Error.Retry", obs_module_text("Error.Retry"));
const char *translError;
std::string errorKey = "ErrorCode." + errorText.ToString();
if (obs_module_get_string(errorKey.c_str(), (const char **)&translError)) {
dstr_replace(&html, "%%ERROR_CODE%%", translError);
} else {
dstr_replace(&html, "%%ERROR_CODE%%", errorText.ToString().c_str());
}
frame->LoadURL("data:text/html;base64," +
CefURIEncode(CefBase64Encode(html.array, html.len), false).ToString());
dstr_free(&html);
bfree(path);
bfree(errorPage);
}
/* CefLifeSpanHandler */
bool QCefBrowserClient::OnBeforePopup(CefRefPtr<CefBrowser>, CefRefPtr<CefFrame>,
#if CHROME_VERSION_BUILD >= 6834
int,
#endif
const CefString &target_url, const CefString &,
CefLifeSpanHandler::WindowOpenDisposition, bool, const CefPopupFeatures &,
CefWindowInfo &windowInfo, CefRefPtr<CefClient> &, CefBrowserSettings &,
CefRefPtr<CefDictionaryValue> &, bool *)
{
if (allowAllPopups) {
#ifdef _WIN32
HWND hwnd = (HWND)widget->effectiveWinId();
windowInfo.parent_window = hwnd;
#else
UNUSED_PARAMETER(windowInfo);
#endif
return false;
}
std::string str_url = target_url;
std::lock_guard<std::mutex> lock(popup_whitelist_mutex);
for (size_t i = popup_whitelist.size(); i > 0; i--) {
PopupWhitelistInfo &info = popup_whitelist[i - 1];
if (!info.obj) {
popup_whitelist.erase(popup_whitelist.begin() + (i - 1));
continue;
}
if (astrcmpi(info.url.c_str(), str_url.c_str()) == 0) {
#ifdef _WIN32
HWND hwnd = (HWND)widget->effectiveWinId();
windowInfo.parent_window = hwnd;
#endif
return false;
}
}
/* Open popup URLs in user's actual browser */
QUrl url = QUrl(str_url.c_str(), QUrl::TolerantMode);
QDesktopServices::openUrl(url);
return true;
}
void QCefBrowserClient::OnBeforeClose(CefRefPtr<CefBrowser>)
{
if (widget) {
widget->CloseSafely();
}
}
bool QCefBrowserClient::OnSetFocus(CefRefPtr<CefBrowser>, CefFocusHandler::FocusSource source)
{
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
/* Workaround for browser docks flashing/hanging at startup with Qt 6.8.x, introduced
* by commit https://code.qt.io/cgit/qt/qt5.git/commit/?id=bab1fecd556ea561c4a89686293116741acfa1b4.
* Refer to https://bugreports.qt.io/browse/QTBUG-136165.
*/
UNUSED_PARAMETER(source);
return false;
#else
/* Don't steal focus when the webpage navigates. This is especially
obvious on startup when the user has many browser docks defined,
as each one will steal focus one by one, resulting in poor UX.
*/
switch (source) {
case FOCUS_SOURCE_NAVIGATION:
return true;
default:
return false;
}
#endif
}
void QCefBrowserClient::OnBeforeContextMenu(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame>,
CefRefPtr<CefContextMenuParams>, CefRefPtr<CefMenuModel> model)
{
if (model->IsVisible(MENU_ID_BACK) &&
(!model->IsVisible(MENU_ID_RELOAD) && !model->IsVisible(MENU_ID_RELOAD_NOCACHE))) {
model->InsertItemAt(2, MENU_ID_RELOAD_NOCACHE, QObject::tr("RefreshBrowser").toUtf8().constData());
}
if (model->IsVisible(MENU_ID_PRINT)) {
model->Remove(MENU_ID_PRINT);
}
if (model->IsVisible(MENU_ID_VIEW_SOURCE)) {
model->Remove(MENU_ID_VIEW_SOURCE);
}
model->AddItem(MENU_ITEM_ZOOM_IN, obs_module_text("Zoom.In"));
if (browser->GetHost()->GetZoomLevel() != 0) {
model->AddItem(MENU_ITEM_ZOOM_RESET, obs_module_text("Zoom.Reset"));
}
model->AddItem(MENU_ITEM_ZOOM_OUT, obs_module_text("Zoom.Out"));
model->AddSeparator();
model->InsertItemAt(model->GetCount(), MENU_ITEM_COPY_URL, obs_module_text("CopyUrl"));
model->InsertItemAt(model->GetCount(), MENU_ITEM_DEVTOOLS, obs_module_text("Inspect"));
model->InsertCheckItemAt(model->GetCount(), MENU_ITEM_MUTE, QObject::tr("Mute").toUtf8().constData());
model->SetChecked(MENU_ITEM_MUTE, browser->GetHost()->IsAudioMuted());
}
#if defined(_WIN32)
bool QCefBrowserClient::RunContextMenu(CefRefPtr<CefBrowser>, CefRefPtr<CefFrame>, CefRefPtr<CefContextMenuParams>,
CefRefPtr<CefMenuModel> model, CefRefPtr<CefRunContextMenuCallback> callback)
{
std::vector<std::tuple<std::string, int, bool, int, bool>> menu_items;
menu_items.reserve(model->GetCount());
for (int i = 0; i < model->GetCount(); i++) {
menu_items.push_back({model->GetLabelAt(i), model->GetCommandIdAt(i), model->IsEnabledAt(i),
model->GetTypeAt(i), model->IsCheckedAt(i)});
}
QMetaObject::invokeMethod(QCoreApplication::instance()->thread(), [menu_items, callback]() {
QMenu contextMenu;
std::string name;
int command_id;
bool enabled;
int type_id;
bool check;
for (const std::tuple<std::string, int, bool, int, bool> &menu_item : menu_items) {
std::tie(name, command_id, enabled, type_id, check) = menu_item;
switch (type_id) {
case MENUITEMTYPE_CHECK:
case MENUITEMTYPE_COMMAND: {
QAction *item = new QAction(name.c_str());
item->setEnabled(enabled);
if (type_id == MENUITEMTYPE_CHECK) {
item->setCheckable(true);
item->setChecked(check);
}
item->setProperty("cmd_id", command_id);
contextMenu.addAction(item);
} break;
case MENUITEMTYPE_SEPARATOR:
contextMenu.addSeparator();
break;
}
}
QAction *action = contextMenu.exec(QCursor::pos());
if (action) {
QVariant cmdId = action->property("cmd_id");
callback.get()->Continue(cmdId.toInt(), EVENTFLAG_NONE);
} else {
callback.get()->Cancel();
}
});
return true;
}
#endif
bool QCefBrowserClient::OnContextMenuCommand(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame>,
CefRefPtr<CefContextMenuParams> params, int command_id,
CefContextMenuHandler::EventFlags)
{
if (command_id < MENU_ID_CUSTOM_FIRST)
return false;
CefRefPtr<CefBrowserHost> host = browser->GetHost();
CefWindowInfo windowInfo;
QPoint pos;
switch (command_id) {
case MENU_ITEM_DEVTOOLS:
#if defined(_WIN32) && CHROME_VERSION_BUILD < 6533
windowInfo.SetAsPopup(host->GetWindowHandle(), "");
#endif
pos = widget->mapToGlobal(QPoint(0, 0));
windowInfo.bounds.x = pos.x();
windowInfo.bounds.y = pos.y() + 30;
windowInfo.bounds.width = 900;
windowInfo.bounds.height = 700;
host->ShowDevTools(windowInfo, host->GetClient(), CefBrowserSettings(),
{params.get()->GetXCoord(), params.get()->GetYCoord()});
return true;
case MENU_ITEM_MUTE:
host->SetAudioMuted(!host->IsAudioMuted());
return true;
case MENU_ITEM_ZOOM_IN:
widget->zoomPage(1);
return true;
case MENU_ITEM_ZOOM_RESET:
widget->zoomPage(0);
return true;
case MENU_ITEM_ZOOM_OUT:
widget->zoomPage(-1);
return true;
case MENU_ITEM_COPY_URL:
std::string url = browser->GetMainFrame()->GetURL().ToString();
auto saveClipboard = [url]() {
QClipboard *clipboard = QApplication::clipboard();
clipboard->setText(url.c_str(), QClipboard::Clipboard);
if (clipboard->supportsSelection()) {
clipboard->setText(url.c_str(), QClipboard::Selection);
}
};
QMetaObject::invokeMethod(QCoreApplication::instance()->thread(), saveClipboard);
return true;
break;
}
return false;
}
void QCefBrowserClient::OnLoadStart(CefRefPtr<CefBrowser>, CefRefPtr<CefFrame> frame, TransitionType)
{
if (!frame->IsMain())
return;
std::string script = "window.close = () => ";
script += "console.log(";
script += "'OBS browser docks cannot be closed using JavaScript.'";
script += ");";
frame->ExecuteJavaScript(script, "", 0);
}
void QCefBrowserClient::OnLoadEnd(CefRefPtr<CefBrowser>, CefRefPtr<CefFrame> frame, int)
{
if (!frame->IsMain())
return;
if (widget && !widget->script.empty())
frame->ExecuteJavaScript(widget->script, CefString(), 0);
else if (!script.empty())
frame->ExecuteJavaScript(script, CefString(), 0);
}
bool QCefBrowserClient::OnJSDialog(CefRefPtr<CefBrowser>, const CefString &,
CefJSDialogHandler::JSDialogType dialog_type, const CefString &message_text,
const CefString &default_prompt_text, CefRefPtr<CefJSDialogCallback> callback,
bool &)
{
QString parentTitle = widget->parentWidget()->windowTitle();
std::string default_value = default_prompt_text;
QString msg_raw(message_text.ToString().c_str());
// Replace <br> with standard newline as we will render in plaintext
msg_raw.replace(QRegularExpression("<br\\s{0,1}\\/{0,1}>"), "\n");
QString submsg = QString(obs_module_text("Dialog.ReceivedFrom")).arg(parentTitle);
QString msg = QString("%1\n\n\n%2").arg(msg_raw).arg(submsg);
if (dialog_type == JSDIALOGTYPE_PROMPT) {
auto msgbox = [msg, default_value, callback]() {
QInputDialog *dlg = new QInputDialog(nullptr);
dlg->setWindowFlag(Qt::WindowStaysOnTopHint, true);
dlg->setWindowFlag(Qt::WindowContextHelpButtonHint, false);
std::stringstream title;
title << obs_module_text("Dialog.Prompt") << ": " << obs_module_text("Dialog.BrowserDock");
dlg->setWindowTitle(title.str().c_str());
if (!default_value.empty())
dlg->setTextValue(default_value.c_str());
auto finished = [callback, dlg](int result) {
callback.get()->Continue(result == QDialog::Accepted,
dlg->textValue().toUtf8().constData());
};
QWidget::connect(dlg, &QInputDialog::finished, finished);
dlg->open();
if (QLabel *lbl = dlg->findChild<QLabel *>()) {
// Force plaintext manually
lbl->setTextFormat(Qt::PlainText);
}
dlg->setLabelText(msg);
};
QMetaObject::invokeMethod(QCoreApplication::instance()->thread(), msgbox);
return true;
}
auto msgbox = [msg, dialog_type, callback]() {
QMessageBox *dlg = new QMessageBox(nullptr);
dlg->setStandardButtons(QMessageBox::Ok);
dlg->setWindowFlag(Qt::WindowStaysOnTopHint, true);
dlg->setTextFormat(Qt::PlainText);
dlg->setText(msg);
std::stringstream title;
switch (dialog_type) {
case JSDIALOGTYPE_CONFIRM:
title << obs_module_text("Dialog.Confirm");
dlg->setIcon(QMessageBox::Question);
dlg->addButton(QMessageBox::Cancel);
break;
case JSDIALOGTYPE_ALERT:
default:
title << obs_module_text("Dialog.Alert");
dlg->setIcon(QMessageBox::Information);
break;
}
title << ": " << obs_module_text("Dialog.BrowserDock");
dlg->setWindowTitle(title.str().c_str());
auto finished = [callback](int result) {
callback.get()->Continue(result == QMessageBox::Ok, "");
};
QWidget::connect(dlg, &QMessageBox::finished, finished);
dlg->open();
};
QMetaObject::invokeMethod(QCoreApplication::instance()->thread(), msgbox);
return true;
}
bool QCefBrowserClient::OnPreKeyEvent(CefRefPtr<CefBrowser> browser, const CefKeyEvent &event, CefEventHandle, bool *)
{
if (event.type != KEYEVENT_RAWKEYDOWN)
return false;
if (event.windows_key_code == 'R' &&
#ifdef __APPLE__
(event.modifiers & EVENTFLAG_COMMAND_DOWN) != 0) {
#else
(event.modifiers & EVENTFLAG_CONTROL_DOWN) != 0) {
#endif
browser->ReloadIgnoreCache();
return true;
} else if ((event.windows_key_code == 189 || event.windows_key_code == 109) &&
(event.modifiers & EVENTFLAG_CONTROL_DOWN) != 0) {
// Zoom out
return widget->zoomPage(-1);
} else if ((event.windows_key_code == 187 || event.windows_key_code == 107) &&
(event.modifiers & EVENTFLAG_CONTROL_DOWN) != 0) {
// Zoom in
return widget->zoomPage(1);
} else if ((event.windows_key_code == 48 || event.windows_key_code == 96) &&
(event.modifiers & EVENTFLAG_CONTROL_DOWN) != 0) {
// Reset zoom
return widget->zoomPage(0);
}
return false;
}
0707010000008E000081A400000000000000000000000168D5836B000010A4000000000000000000000000000000000000005400000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/panel/browser-panel-client.hpp#pragma once
#include "cef-headers.hpp"
#include "browser-panel-internal.hpp"
#include <string>
class QCefBrowserClient : public CefClient,
public CefDisplayHandler,
public CefRequestHandler,
public CefLifeSpanHandler,
public CefContextMenuHandler,
public CefLoadHandler,
public CefKeyboardHandler,
public CefFocusHandler,
public CefJSDialogHandler {
public:
inline QCefBrowserClient(QCefWidgetInternal *widget_, const std::string &script_, bool allowAllPopups_)
: widget(widget_),
script(script_),
allowAllPopups(allowAllPopups_)
{
}
/* CefClient */
virtual CefRefPtr<CefLoadHandler> GetLoadHandler() override;
virtual CefRefPtr<CefDisplayHandler> GetDisplayHandler() override;
virtual CefRefPtr<CefRequestHandler> GetRequestHandler() override;
virtual CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() override;
virtual CefRefPtr<CefKeyboardHandler> GetKeyboardHandler() override;
virtual CefRefPtr<CefFocusHandler> GetFocusHandler() override;
virtual CefRefPtr<CefContextMenuHandler> GetContextMenuHandler() override;
virtual CefRefPtr<CefJSDialogHandler> GetJSDialogHandler() override;
/* CefDisplayHandler */
virtual void OnTitleChange(CefRefPtr<CefBrowser> browser, const CefString &title) override;
/* CefRequestHandler */
virtual bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request, bool user_gesture, bool is_redirect) override;
virtual void OnLoadError(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame,
CefLoadHandler::ErrorCode errorCode, const CefString &errorText,
const CefString &failedUrl) override;
virtual bool OnOpenURLFromTab(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame,
const CefString &target_url,
CefRequestHandler::WindowOpenDisposition target_disposition,
bool user_gesture) override;
/* CefLifeSpanHandler */
virtual bool OnBeforePopup(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame,
#if CHROME_VERSION_BUILD >= 6834
int popup_id,
#endif
const CefString &target_url, const CefString &target_frame_name,
CefLifeSpanHandler::WindowOpenDisposition target_disposition, bool user_gesture,
const CefPopupFeatures &popupFeatures, CefWindowInfo &windowInfo,
CefRefPtr<CefClient> &client, CefBrowserSettings &settings,
CefRefPtr<CefDictionaryValue> &extra_info, bool *no_javascript_access) override;
virtual void OnBeforeClose(CefRefPtr<CefBrowser> browser) override;
/* CefFocusHandler */
virtual bool OnSetFocus(CefRefPtr<CefBrowser> browser, CefFocusHandler::FocusSource source) override;
/* CefContextMenuHandler */
virtual void OnBeforeContextMenu(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame,
CefRefPtr<CefContextMenuParams> params,
CefRefPtr<CefMenuModel> model) override;
#if defined(_WIN32)
virtual bool RunContextMenu(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame,
CefRefPtr<CefContextMenuParams> params, CefRefPtr<CefMenuModel> model,
CefRefPtr<CefRunContextMenuCallback> callback) override;
#endif
virtual bool OnContextMenuCommand(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame,
CefRefPtr<CefContextMenuParams> params, int command_id,
CefContextMenuHandler::EventFlags event_flags) override;
/* CefLoadHandler */
virtual void OnLoadStart(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame,
TransitionType transition_type) override;
virtual void OnLoadEnd(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, int httpStatusCode) override;
/* CefKeyboardHandler */
virtual bool OnPreKeyEvent(CefRefPtr<CefBrowser> browser, const CefKeyEvent &event, CefEventHandle os_event,
bool *is_keyboard_shortcut) override;
/* CefJSDialogHandler */
virtual bool OnJSDialog(CefRefPtr<CefBrowser> browser, const CefString &origin_url,
CefJSDialogHandler::JSDialogType dialog_type, const CefString &message_text,
const CefString &default_prompt_text, CefRefPtr<CefJSDialogCallback> callback,
bool &suppress_message) override;
QCefWidgetInternal *widget = nullptr;
std::string script;
bool allowAllPopups;
IMPLEMENT_REFCOUNTING(QCefBrowserClient);
};
0707010000008F000081A400000000000000000000000168D5836B000006A9000000000000000000000000000000000000005600000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/panel/browser-panel-internal.hpp#pragma once
#include <QTimer>
#include <QPointer>
#include "browser-panel.hpp"
#include "cef-headers.hpp"
#include <vector>
#include <mutex>
struct PopupWhitelistInfo {
std::string url;
QPointer<QObject> obj;
inline PopupWhitelistInfo(const std::string &url_, QObject *obj_) : url(url_), obj(obj_) {}
};
extern std::mutex popup_whitelist_mutex;
extern std::vector<PopupWhitelistInfo> popup_whitelist;
extern std::vector<PopupWhitelistInfo> forced_popups;
/* ------------------------------------------------------------------------- */
class QCefWidgetInternal : public QCefWidget {
Q_OBJECT
public:
QCefWidgetInternal(QWidget *parent, const std::string &url, CefRefPtr<CefRequestContext> rqc);
~QCefWidgetInternal();
CefRefPtr<CefBrowser> cefBrowser;
std::string url;
std::string script;
CefRefPtr<CefRequestContext> rqc;
QTimer timer;
#ifndef __APPLE__
QPointer<QWindow> window;
QPointer<QWidget> container;
#endif
bool allowAllPopups_ = false;
virtual void resizeEvent(QResizeEvent *event) override;
virtual void showEvent(QShowEvent *event) override;
virtual QPaintEngine *paintEngine() const override;
virtual void setURL(const std::string &url) override;
virtual void setStartupScript(const std::string &script) override;
virtual void allowAllPopups(bool allow) override;
virtual void closeBrowser() override;
virtual void reloadPage() override;
virtual bool zoomPage(int direction) override;
virtual void executeJavaScript(const std::string &script) override;
void CloseSafely();
void Resize();
#ifdef __linux__
private:
bool needsDeleteXdndProxy = true;
void unsetToplevelXdndProxy();
#endif
public slots:
void Init();
signals:
void readyToClose();
};
07070100000090000081A400000000000000000000000168D5836B000039C8000000000000000000000000000000000000004D00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/panel/browser-panel.cpp#include "browser-panel-internal.hpp"
#include "browser-panel-client.hpp"
#include "cef-headers.hpp"
#include "browser-app.hpp"
#include <QWindow>
#include <QApplication>
#ifdef ENABLE_BROWSER_QT_LOOP
#include <QEventLoop>
#include <QThread>
#endif
#ifdef __APPLE__
#include <objc/objc.h>
#endif
#include <obs-module.h>
#include <util/threading.h>
#include <util/base.h>
#include <thread>
#include <cmath>
#if !defined(_WIN32) && !defined(__APPLE__)
#include <X11/Xlib.h>
#endif
extern bool QueueCEFTask(std::function<void()> task);
extern "C" void obs_browser_initialize(void);
extern os_event_t *cef_started_event;
std::mutex popup_whitelist_mutex;
std::vector<PopupWhitelistInfo> popup_whitelist;
std::vector<PopupWhitelistInfo> forced_popups;
static int zoomLvls[] = {25, 33, 50, 67, 75, 80, 90, 100, 110, 125, 150, 175, 200, 250, 300, 400};
/* ------------------------------------------------------------------------- */
class CookieCheck : public CefCookieVisitor {
public:
QCefCookieManager::cookie_exists_cb callback;
std::string target;
bool cookie_found = false;
inline CookieCheck(QCefCookieManager::cookie_exists_cb callback_, const std::string target_)
: callback(callback_),
target(target_)
{
}
virtual ~CookieCheck() { callback(cookie_found); }
virtual bool Visit(const CefCookie &cookie, int, int, bool &) override
{
CefString cef_name = cookie.name.str;
std::string name = cef_name;
if (name == target) {
cookie_found = true;
return false;
}
return true;
}
IMPLEMENT_REFCOUNTING(CookieCheck);
};
struct QCefCookieManagerInternal : QCefCookieManager {
CefRefPtr<CefCookieManager> cm;
CefRefPtr<CefRequestContext> rc;
QCefCookieManagerInternal(const std::string &storage_path, bool persist_session_cookies)
{
if (os_event_try(cef_started_event) != 0)
throw "Browser thread not initialized";
BPtr<char> rpath = obs_module_config_path(storage_path.c_str());
if (os_mkdirs(rpath.Get()) == MKDIR_ERROR)
throw "Failed to create cookie directory";
BPtr<char> path = os_get_abs_path_ptr(rpath.Get());
CefRequestContextSettings settings;
#if CHROME_VERSION_BUILD <= 6533
settings.persist_user_preferences = 1;
#endif
CefString(&settings.cache_path) = path.Get();
rc = CefRequestContext::CreateContext(settings, CefRefPtr<CefRequestContextHandler>());
if (rc)
cm = rc->GetCookieManager(nullptr);
UNUSED_PARAMETER(persist_session_cookies);
}
virtual bool DeleteCookies(const std::string &url, const std::string &name) override
{
return !!cm ? cm->DeleteCookies(url, name, nullptr) : false;
}
virtual bool SetStoragePath(const std::string &storage_path, bool persist_session_cookies) override
{
BPtr<char> rpath = obs_module_config_path(storage_path.c_str());
BPtr<char> path = os_get_abs_path_ptr(rpath.Get());
CefRequestContextSettings settings;
#if CHROME_VERSION_BUILD <= 6533
settings.persist_user_preferences = 1;
#endif
CefString(&settings.cache_path) = storage_path;
rc = CefRequestContext::CreateContext(settings, CefRefPtr<CefRequestContextHandler>());
if (rc)
cm = rc->GetCookieManager(nullptr);
UNUSED_PARAMETER(persist_session_cookies);
return true;
}
virtual bool FlushStore() override { return !!cm ? cm->FlushStore(nullptr) : false; }
virtual void CheckForCookie(const std::string &site, const std::string &cookie,
cookie_exists_cb callback) override
{
if (!cm)
return;
CefRefPtr<CookieCheck> c = new CookieCheck(callback, cookie);
cm->VisitUrlCookies(site, false, c);
}
};
/* ------------------------------------------------------------------------- */
QCefWidgetInternal::QCefWidgetInternal(QWidget *parent, const std::string &url_, CefRefPtr<CefRequestContext> rqc_)
: QCefWidget(parent),
url(url_),
rqc(rqc_)
{
setAttribute(Qt::WA_PaintOnScreen);
setAttribute(Qt::WA_StaticContents);
setAttribute(Qt::WA_NoSystemBackground);
setAttribute(Qt::WA_OpaquePaintEvent);
setAttribute(Qt::WA_DontCreateNativeAncestors);
setAttribute(Qt::WA_NativeWindow);
setFocusPolicy(Qt::ClickFocus);
#ifndef __APPLE__
window = new QWindow();
window->setFlags(Qt::FramelessWindowHint);
#endif
}
QCefWidgetInternal::~QCefWidgetInternal()
{
closeBrowser();
}
void QCefWidgetInternal::closeBrowser()
{
CefRefPtr<CefBrowser> browser = cefBrowser;
if (!!browser) {
auto destroyBrowser = [=](CefRefPtr<CefBrowser> cefBrowser) {
CefRefPtr<CefClient> client = cefBrowser->GetHost()->GetClient();
QCefBrowserClient *bc = reinterpret_cast<QCefBrowserClient *>(client.get());
cefBrowser->GetHost()->CloseBrowser(true);
#if CHROME_VERSION_BUILD >= 6533
QEventLoop loop;
connect(this, &QCefWidgetInternal::readyToClose, &loop, &QEventLoop::quit);
QTimer::singleShot(1000, &loop, &QEventLoop::quit);
loop.exec();
#endif
if (bc) {
bc->widget = nullptr;
}
};
/* So you're probably wondering what's going on here. If you
* call CefBrowserHost::CloseBrowser, and it fails to unload
* the web page *before* WM_NCDESTROY is called on the browser
* HWND, it will call an internal CEF function
* CefBrowserPlatformDelegateNativeWin::CloseHostWindow, which
* will attempt to close the browser's main window itself.
* Problem is, this closes the root window containing the
* browser's HWND rather than the browser's specific HWND for
* whatever mysterious reason. If the browser is in a dock
* widget, then the window it closes is, unfortunately, the
* main program's window, causing the entire program to shut
* down.
*
* So, instead, before closing the browser, we need to decouple
* the browser from the widget. To do this, we hide it, then
* remove its parent. */
#ifdef _WIN32
HWND hwnd = (HWND)cefBrowser->GetHost()->GetWindowHandle();
if (hwnd) {
ShowWindow(hwnd, SW_HIDE);
SetParent(hwnd, nullptr);
}
#elif __APPLE__
// felt hacky, might delete later
void *view = (id)cefBrowser->GetHost()->GetWindowHandle();
if (*((bool *)view))
((void (*)(id, SEL))objc_msgSend)((id)view, sel_getUid("removeFromSuperview"));
#endif
destroyBrowser(browser);
browser = nullptr;
cefBrowser = nullptr;
}
}
#ifdef __linux__
static bool XWindowHasAtom(Display *display, Window w, Atom a)
{
Atom type;
int format;
unsigned long nItems;
unsigned long bytesAfter;
unsigned char *data = NULL;
if (XGetWindowProperty(display, w, a, 0, LONG_MAX, False, AnyPropertyType, &type, &format, &nItems, &bytesAfter,
&data) != Success)
return false;
if (data)
XFree(data);
return type != None;
}
/* On Linux / X11, CEF sets the XdndProxy of the toplevel window
* it's attached to, so that it can read drag events. When this
* toplevel happens to be OBS Studio's main window (e.g. when a
* browser panel is docked into to the main window), setting the
* XdndProxy atom ends up breaking DnD of sources and scenes. Thus,
* we have to manually unset this atom.
*/
void QCefWidgetInternal::unsetToplevelXdndProxy()
{
if (!cefBrowser)
return;
CefWindowHandle browserHandle = cefBrowser->GetHost()->GetWindowHandle();
Display *xDisplay = cef_get_xdisplay();
Window toplevel, root, parent, *children;
unsigned int nChildren;
bool found = false;
toplevel = browserHandle;
// Find the toplevel
Atom netWmPidAtom = XInternAtom(xDisplay, "_NET_WM_PID", False);
do {
if (XQueryTree(xDisplay, toplevel, &root, &parent, &children, &nChildren) == 0)
return;
if (children)
XFree(children);
if (root == parent || !XWindowHasAtom(xDisplay, parent, netWmPidAtom)) {
found = true;
break;
}
toplevel = parent;
} while (true);
if (!found)
return;
// Check if the XdndProxy property is set
Atom xDndProxyAtom = XInternAtom(xDisplay, "XdndProxy", False);
if (needsDeleteXdndProxy && !XWindowHasAtom(xDisplay, toplevel, xDndProxyAtom)) {
QueueCEFTask([this]() { unsetToplevelXdndProxy(); });
return;
}
XDeleteProperty(xDisplay, toplevel, xDndProxyAtom);
needsDeleteXdndProxy = false;
}
#endif
void QCefWidgetInternal::Init()
{
#ifndef __APPLE__
WId handle = window->winId();
QSize size = this->size();
size *= devicePixelRatioF();
bool success = QueueCEFTask(
[this, handle, size]()
#else
WId handle = winId();
bool success = QueueCEFTask(
[this, handle]()
#endif
{
CefWindowInfo windowInfo;
/* Make sure Init isn't called more than once. */
if (cefBrowser)
return;
#ifdef __APPLE__
QSize size = this->size();
#endif
#if CHROME_VERSION_BUILD >= 6533
windowInfo.runtime_style = CEF_RUNTIME_STYLE_ALLOY;
#endif
windowInfo.SetAsChild((CefWindowHandle)handle, CefRect(0, 0, size.width(), size.height()));
CefRefPtr<QCefBrowserClient> browserClient =
new QCefBrowserClient(this, script, allowAllPopups_);
CefBrowserSettings cefBrowserSettings;
cefBrowser = CefBrowserHost::CreateBrowserSync(windowInfo, browserClient, url,
cefBrowserSettings,
CefRefPtr<CefDictionaryValue>(), rqc);
#ifdef __linux__
QueueCEFTask([this]() { unsetToplevelXdndProxy(); });
#endif
});
if (success) {
timer.stop();
#ifndef __APPLE__
if (!container) {
container = QWidget::createWindowContainer(window, this);
container->show();
}
Resize();
#endif
}
}
void QCefWidgetInternal::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
#ifndef __APPLE__
Resize();
}
void QCefWidgetInternal::Resize()
{
QSize size = this->size() * devicePixelRatioF();
bool success = QueueCEFTask([this, size]() {
if (!cefBrowser)
return;
CefWindowHandle handle = cefBrowser->GetHost()->GetWindowHandle();
if (!handle)
return;
#ifdef _WIN32
SetWindowPos((HWND)handle, nullptr, 0, 0, size.width(), size.height(),
SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER);
SendMessage((HWND)handle, WM_SIZE, 0, MAKELPARAM(size.width(), size.height()));
#else
Display *xDisplay = cef_get_xdisplay();
if (!xDisplay)
return;
XWindowChanges changes = {0};
changes.x = 0;
changes.y = 0;
changes.width = size.width();
changes.height = size.height();
XConfigureWindow(xDisplay, (Window)handle, CWX | CWY | CWHeight | CWWidth, &changes);
#if CHROME_VERSION_BUILD >= 4638
XSync(xDisplay, false);
#endif
#endif
});
if (success && container)
container->resize(size.width(), size.height());
#endif
}
void QCefWidgetInternal::CloseSafely()
{
emit readyToClose();
}
void QCefWidgetInternal::showEvent(QShowEvent *event)
{
QWidget::showEvent(event);
if (!cefBrowser) {
obs_browser_initialize();
connect(&timer, &QTimer::timeout, this, &QCefWidgetInternal::Init);
timer.start(500);
Init();
}
}
QPaintEngine *QCefWidgetInternal::paintEngine() const
{
return nullptr;
}
void QCefWidgetInternal::setURL(const std::string &url_)
{
url = url_;
if (cefBrowser) {
cefBrowser->GetMainFrame()->LoadURL(url);
}
}
void QCefWidgetInternal::reloadPage()
{
if (cefBrowser)
cefBrowser->ReloadIgnoreCache();
}
void QCefWidgetInternal::setStartupScript(const std::string &script_)
{
script = script_;
}
void QCefWidgetInternal::executeJavaScript(const std::string &script_)
{
if (!cefBrowser)
return;
CefRefPtr<CefFrame> frame = cefBrowser->GetMainFrame();
std::string url = frame->GetURL();
frame->ExecuteJavaScript(script_, url, 0);
}
void QCefWidgetInternal::allowAllPopups(bool allow)
{
allowAllPopups_ = allow;
}
bool QCefWidgetInternal::zoomPage(int direction)
{
if (!cefBrowser || direction < -1 || direction > 1)
return false;
CefRefPtr<CefBrowserHost> host = cefBrowser->GetHost();
if (direction == 0) {
// Reset zoom
host->SetZoomLevel(0);
return true;
}
int currentZoomPercent = round(pow(1.2, host->GetZoomLevel()) * 100.0);
int zoomCount = sizeof(zoomLvls) / sizeof(zoomLvls[0]);
int zoomIdx = 0;
while (zoomIdx < zoomCount) {
if (zoomLvls[zoomIdx] == currentZoomPercent) {
break;
}
zoomIdx++;
}
if (zoomIdx == zoomCount)
return false;
int newZoomIdx = zoomIdx;
if (direction == -1 && zoomIdx > 0) {
// Zoom out
newZoomIdx -= 1;
} else if (direction == 1 && zoomIdx >= 0 && zoomIdx < zoomCount - 1) {
// Zoom in
newZoomIdx += 1;
}
if (newZoomIdx != zoomIdx) {
int newZoomLvl = zoomLvls[newZoomIdx];
// SetZoomLevel only accepts a zoomLevel, not a percentage
host->SetZoomLevel(log(newZoomLvl / 100.0) / log(1.2));
return true;
}
return false;
}
/* ------------------------------------------------------------------------- */
struct QCefInternal : QCef {
virtual bool init_browser(void) override;
virtual bool initialized(void) override;
virtual bool wait_for_browser_init(void) override;
virtual QCefWidget *create_widget(QWidget *parent, const std::string &url,
QCefCookieManager *cookie_manager) override;
virtual QCefCookieManager *create_cookie_manager(const std::string &storage_path,
bool persist_session_cookies) override;
virtual BPtr<char> get_cookie_path(const std::string &storage_path) override;
virtual void add_popup_whitelist_url(const std::string &url, QObject *obj) override;
virtual void add_force_popup_url(const std::string &url, QObject *obj) override;
};
bool QCefInternal::init_browser(void)
{
if (os_event_try(cef_started_event) == 0)
return true;
obs_browser_initialize();
return false;
}
bool QCefInternal::initialized(void)
{
return os_event_try(cef_started_event) == 0;
}
bool QCefInternal::wait_for_browser_init(void)
{
return os_event_wait(cef_started_event) == 0;
}
QCefWidget *QCefInternal::create_widget(QWidget *parent, const std::string &url, QCefCookieManager *cm)
{
QCefCookieManagerInternal *cmi = reinterpret_cast<QCefCookieManagerInternal *>(cm);
return new QCefWidgetInternal(parent, url, cmi ? cmi->rc : nullptr);
}
QCefCookieManager *QCefInternal::create_cookie_manager(const std::string &storage_path, bool persist_session_cookies)
{
try {
return new QCefCookieManagerInternal(storage_path, persist_session_cookies);
} catch (const char *error) {
blog(LOG_ERROR, "Failed to create cookie manager: %s", error);
return nullptr;
}
}
BPtr<char> QCefInternal::get_cookie_path(const std::string &storage_path)
{
BPtr<char> rpath = obs_module_config_path(storage_path.c_str());
return os_get_abs_path_ptr(rpath.Get());
}
void QCefInternal::add_popup_whitelist_url(const std::string &url, QObject *obj)
{
std::lock_guard<std::mutex> lock(popup_whitelist_mutex);
popup_whitelist.emplace_back(url, obj);
}
void QCefInternal::add_force_popup_url(const std::string &url, QObject *obj)
{
std::lock_guard<std::mutex> lock(popup_whitelist_mutex);
forced_popups.emplace_back(url, obj);
}
extern "C" EXPORT QCef *obs_browser_create_qcef(void)
{
return new QCefInternal();
}
#define BROWSER_PANEL_VERSION 3
extern "C" EXPORT int obs_browser_qcef_version_export(void)
{
return BROWSER_PANEL_VERSION;
}
07070100000091000081A400000000000000000000000168D5836B00000BF8000000000000000000000000000000000000004D00000000obs-browser-4056a311780ef0129ca099f1fb6e113ac3ed0032/panel/browser-panel.hpp#pragma once
#include <obs-module.h>
#include <util/platform.h>
#include <util/util.hpp>
#include <QWidget>
#include <functional>
#include <string>
#if defined(__APPLE__)
#include <dlfcn.h>
#endif
#ifdef ENABLE_WAYLAND
#include <obs-nix-platform.h>
#endif
struct QCefCookieManager {
virtual ~QCefCookieManager() {}
virtual bool DeleteCookies(const std::string &url, const std::string &name) = 0;
virtual bool SetStoragePath(const std::string &storage_path, bool persist_session_cookies = false) = 0;
virtual bool FlushStore() = 0;
typedef std::function<void(bool)> cookie_exists_cb;
virtual void CheckForCookie(const std::string &site, const std::string &cookie, cookie_exists_cb callback) = 0;
};
/* ------------------------------------------------------------------------- */
class QCefWidget : public QWidget {
Q_OBJECT
protected:
inline QCefWidget(QWidget *parent) : QWidget(parent) {}
public:
virtual void setURL(const std::string &url) = 0;
virtual void setStartupScript(const std::string &script) = 0;
virtual void allowAllPopups(bool allow) = 0;
virtual void closeBrowser() = 0;
virtual void reloadPage() = 0;
virtual bool zoomPage(int direction) = 0;
virtual void executeJavaScript(const std::string &script) = 0;
signals:
void titleChanged(const QString &title);
void urlChanged(const QString &url);
};
/* ------------------------------------------------------------------------- */
struct QCef {
virtual ~QCef() {}
virtual bool init_browser(void) = 0;
virtual bool initialized(void) = 0;
virtual bool wait_for_browser_init(void) = 0;
virtual QCefWidget *create_widget(QWidget *parent, const std::string &url,
QCefCookieManager *cookie_manager = nullptr) = 0;
virtual QCefCookieManager *create_cookie_manager(const std::string &storage_path,
bool persist_session_cookies = false) = 0;
virtual BPtr<char> get_cookie_path(const std::string &storage_path) = 0;
virtual void add_popup_whitelist_url(const std::string &url, QObject *obj) = 0;
virtual void add_force_popup_url(const std::string &url, QObject *obj) = 0;
};
static inline void *get_browser_lib()
{
// Disable panels on Wayland for now
bool isWayland = false;
#ifdef ENABLE_WAYLAND
isWayland = obs_get_nix_platform() == OBS_NIX_PLATFORM_WAYLAND;
#endif
if (isWayland)
return nullptr;
obs_module_t *browserModule = obs_get_module("obs-browser");
if (!browserModule)
return nullptr;
return obs_get_module_lib(browserModule);
}
static inline QCef *obs_browser_init_panel(void)
{
void *lib = get_browser_lib();
QCef *(*create_qcef)(void) = nullptr;
if (!lib)
return nullptr;
create_qcef = (decltype(create_qcef))os_dlsym(lib, "obs_browser_create_qcef");
if (!create_qcef)
return nullptr;
return create_qcef();
}
static inline int obs_browser_qcef_version(void)
{
void *lib = get_browser_lib();
int (*qcef_version)(void) = nullptr;
if (!lib)
return 0;
qcef_version = (decltype(qcef_version))os_dlsym(lib, "obs_browser_qcef_version_export");
if (!qcef_version)
return 0;
return qcef_version();
}
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!815 blocks