CAIIC 定制版 v1.0.0:英文默认界面、2K/4K 支持、集成抠图插件、更换品牌图标
界面与品牌 - 默认 UI 语言固定为英文,不再跟随系统区域设置自动切换(frontend/OBSApp.cpp),用户仍可在设置中手动切换 - 程序图标更换为 CAIIC logo:exe 图标 obs-studio.ico 及界面/托盘/About 图标 obs.png - About 窗口精简为 logo + 名称 + 版本号,移除项目介绍、赞助名单、Authors/License 标签页 - 版本号定为 1.0.0(构建参数 OBS_VERSION_OVERRIDE=1.0.0) 分辨率支持 - 移除首次启动将画布强制限制为 1920x1080 的逻辑,默认使用显示器原生分辨率 - 设置-视频 与自动配置向导新增 3840x2160 / 2560x1440 预设 - 自动配置向导性能测试上限放宽至 4K60(软硬件编码数据率上限及起始分辨率钳制) 插件 - 集成 obs-shaderfilter 2.6.0(绿幕抠图/自定义着色器滤镜),并适配新版 libobs 的 gs_image_file_ex API - 补齐缺失的源码:obs-browser、obs-websocket、libdshowcapture(含 external/capture-device-support) 工程 - 新增 .gitignore,排除 deploy/ 发布目录与构建日志
This commit is contained in:
parent
438b2bada3
commit
d0074858bf
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
/deploy/
|
||||
/build_log.txt
|
||||
/configure_log.txt
|
||||
/icon_preview.png
|
||||
BIN
caiic_logo.jpg
Normal file
BIN
caiic_logo.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 161 KiB |
9
obs-studio/deps/libdshowcapture/src/.gitattributes
vendored
Normal file
9
obs-studio/deps/libdshowcapture/src/.gitattributes
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
* text=auto
|
||||
|
||||
*.sln text eol=crlf
|
||||
*.vcproj text eol=crlf
|
||||
*.vcxproj text eol=crlf
|
||||
*.vcxproj text eol=crlf
|
||||
*.vcxproj.filters text eol=crlf
|
||||
|
||||
cmake/ALL_BUILD.vcxproj.user.in text eol=crlf
|
||||
47
obs-studio/deps/libdshowcapture/src/.gitignore
vendored
Normal file
47
obs-studio/deps/libdshowcapture/src/.gitignore
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
*.exe
|
||||
*.dll
|
||||
|
||||
/release/
|
||||
/debug/
|
||||
/builds/
|
||||
/other/
|
||||
Release_MD/
|
||||
Release/
|
||||
Debug/
|
||||
x64/
|
||||
ipch/
|
||||
GeneratedFiles/
|
||||
|
||||
.hg
|
||||
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
|
||||
*.manifest
|
||||
*.dep
|
||||
*.zip
|
||||
*.lnk
|
||||
*.chm
|
||||
*~
|
||||
3
obs-studio/deps/libdshowcapture/src/.gitmodules
vendored
Normal file
3
obs-studio/deps/libdshowcapture/src/.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "external/capture-device-support"]
|
||||
path = external/capture-device-support
|
||||
url = https://github.com/elgatosf/capture-device-support
|
||||
88
obs-studio/deps/libdshowcapture/src/CMakeLists.txt
Normal file
88
obs-studio/deps/libdshowcapture/src/CMakeLists.txt
Normal file
@ -0,0 +1,88 @@
|
||||
cmake_minimum_required(VERSION 3.28...3.30)
|
||||
|
||||
project(libdshowcapture)
|
||||
|
||||
if(NOT MSVC)
|
||||
message(FATAL_ERROR "libdshowcapture only supports building with Visual Studio and MSVC.")
|
||||
endif()
|
||||
|
||||
option(BUILD_SHARED_LIBS "Build shared library" ON)
|
||||
|
||||
# Set C and C++ language standards to C17 and C++17
|
||||
set(CMAKE_C_STANDARD 17)
|
||||
set(CMAKE_C_STANDARD_REQUIRED TRUE)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
|
||||
|
||||
# Disable C and C++ extensions
|
||||
set(CMAKE_C_EXTENSIONS FALSE)
|
||||
set(CMAKE_CXX_EXTENSIONS FALSE)
|
||||
|
||||
# Set symbols to be hidden by default for C and C++
|
||||
set(CMAKE_C_VISIBILITY_PRESET hidden)
|
||||
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
|
||||
set(CMAKE_VISIBILITY_INLINES_HIDDEN TRUE)
|
||||
|
||||
add_library(libdshowcapture)
|
||||
|
||||
target_sources(
|
||||
libdshowcapture
|
||||
PRIVATE
|
||||
external/capture-device-support/Library/EGAVResult.cpp
|
||||
external/capture-device-support/Library/ElgatoUVCDevice.cpp
|
||||
external/capture-device-support/Library/win/EGAVHIDImplementation.cpp
|
||||
external/capture-device-support/SampleCode/DriverInterface.cpp
|
||||
source/capture-filter.cpp
|
||||
source/output-filter.cpp
|
||||
source/dshowcapture.cpp
|
||||
source/dshowencode.cpp
|
||||
source/device.cpp
|
||||
source/device-vendor.cpp
|
||||
source/encoder.cpp
|
||||
source/dshow-base.cpp
|
||||
source/dshow-demux.cpp
|
||||
source/dshow-enum.cpp
|
||||
source/dshow-formats.cpp
|
||||
source/dshow-media-type.cpp
|
||||
source/dshow-encoded-device.cpp
|
||||
source/log.cpp
|
||||
dshowcapture.hpp
|
||||
source/external/IVideoCaptureFilter.h
|
||||
source/capture-filter.hpp
|
||||
source/output-filter.hpp
|
||||
source/device.hpp
|
||||
source/encoder.hpp
|
||||
source/dshow-base.hpp
|
||||
source/dshow-demux.hpp
|
||||
source/dshow-device-defs.hpp
|
||||
source/dshow-enum.hpp
|
||||
source/dshow-formats.hpp
|
||||
source/dshow-media-type.hpp
|
||||
source/log.hpp
|
||||
)
|
||||
|
||||
# Set /W4
|
||||
# Disable pointless constant condition warnings
|
||||
target_compile_options(libdshowcapture PRIVATE /W4 /wd4127 /wd4201)
|
||||
|
||||
if(CMAKE_VS_PLATFORM_NAME STREQUAL Win32)
|
||||
target_link_options(libdshowcapture PRIVATE /SAFESEH:NO)
|
||||
endif()
|
||||
|
||||
target_include_directories(libdshowcapture PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/external/capture-device-support/Library)
|
||||
|
||||
target_compile_definitions(
|
||||
libdshowcapture
|
||||
PRIVATE
|
||||
UNICODE
|
||||
_UNICODE
|
||||
$<$<CONFIG:DEBUG>:DEBUG>
|
||||
$<$<CONFIG:DEBUG>:_DEBUG>
|
||||
$<$<BOOL:${BUILD_SHARED_LIBS}>:DSHOWCAPTURE_EXPORTS>
|
||||
_UP_WINDOWS=1
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
libdshowcapture
|
||||
PRIVATE setupapi strmiids ksuser winmm wmcodecdspuuid
|
||||
)
|
||||
502
obs-studio/deps/libdshowcapture/src/COPYING
Normal file
502
obs-studio/deps/libdshowcapture/src/COPYING
Normal file
@ -0,0 +1,502 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
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 and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for
|
||||
you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link other code with the library, you must provide
|
||||
complete object files to the recipients, so that they can relink them
|
||||
with the library after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
modified by someone else and passed on, the recipients should know
|
||||
that what they have is not the original version, so that the original
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
effectively restrict the users of a free program by obtaining a
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser General Public License (also called "this License").
|
||||
Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, 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 library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete 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 distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
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 Library or any portion
|
||||
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
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 Library, 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 Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you 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.
|
||||
|
||||
If distribution of 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 satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also combine or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the materials to be 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.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library 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.
|
||||
|
||||
9. 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 Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
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 with
|
||||
this License.
|
||||
|
||||
11. 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 Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library 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 Library.
|
||||
|
||||
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.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library 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.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Lesser 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 Library
|
||||
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 Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
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
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "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
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. 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 LIBRARY 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
|
||||
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
everyone can redistribute and change. You can do so by permitting
|
||||
redistribution under these terms (or, alternatively, under the terms of the
|
||||
ordinary General Public License).
|
||||
|
||||
To apply these terms, attach the following notices to the library. 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 library's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1990
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
||||
11
obs-studio/deps/libdshowcapture/src/README
Normal file
11
obs-studio/deps/libdshowcapture/src/README
Normal file
@ -0,0 +1,11 @@
|
||||
|
||||
libdshowcapture
|
||||
|
||||
This library was created as a means to simplify the process of using
|
||||
DirectShow to capture video and/or audio devices, such as webcams, capture
|
||||
devices (internal, USB 2.0, USB 3.0), microphones, auxiliary sound inputs,
|
||||
etc.
|
||||
|
||||
The biggest goal of this project is to eventually support as many devices as
|
||||
possible, as well as add more interesting features later on for improving
|
||||
performance.
|
||||
306
obs-studio/deps/libdshowcapture/src/dshowcapture.hpp
Normal file
306
obs-studio/deps/libdshowcapture/src/dshowcapture.hpp
Normal file
@ -0,0 +1,306 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Lain Bailey <lain@obsproject.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
|
||||
#ifdef DSHOWCAPTURE_EXPORTS
|
||||
#define DSHOWCAPTURE_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define DSHOWCAPTURE_EXPORT
|
||||
#endif
|
||||
|
||||
#define DSHOWCAPTURE_VERSION_MAJOR 0
|
||||
#define DSHOWCAPTURE_VERSION_MINOR 10
|
||||
#define DSHOWCAPTURE_VERSION_PATCH 0
|
||||
|
||||
#define MAKE_DSHOWCAPTURE_VERSION(major, minor, patch) \
|
||||
((major << 24) | (minor << 16) | (patch))
|
||||
|
||||
#define DSHOWCAPTURE_VERSION \
|
||||
MAKE_DSHOWCAPTURE_VERSION(DSHOWCAPTURE_VERSION_MAJOR, \
|
||||
DSHOWCAPTURE_VERSION_MINOR, \
|
||||
DSHOWCAPTURE_VERSION_PATCH)
|
||||
|
||||
#define DSHOW_MAX_PLANES 8
|
||||
|
||||
namespace DShow {
|
||||
/* internal forward */
|
||||
struct HDevice;
|
||||
struct HVideoEncoder;
|
||||
struct VideoConfig;
|
||||
struct AudioConfig;
|
||||
|
||||
typedef std::function<void(const VideoConfig &config, unsigned char *data,
|
||||
size_t size, long long startTime, long long stopTime,
|
||||
long rotation)>
|
||||
VideoProc;
|
||||
|
||||
typedef std::function<void(const AudioConfig &config, unsigned char *data,
|
||||
size_t size, long long startTime, long long stopTime)>
|
||||
AudioProc;
|
||||
|
||||
typedef std::function<void()> ReactivateProc;
|
||||
|
||||
enum class InitGraph {
|
||||
False,
|
||||
True,
|
||||
};
|
||||
|
||||
/** DirectShow configuration dialog type */
|
||||
enum class DialogType {
|
||||
ConfigVideo,
|
||||
ConfigAudio,
|
||||
ConfigCrossbar,
|
||||
ConfigCrossbar2,
|
||||
};
|
||||
|
||||
enum class VideoFormat {
|
||||
Any,
|
||||
Unknown,
|
||||
|
||||
/* raw formats */
|
||||
ARGB = 100,
|
||||
XRGB,
|
||||
RGB24,
|
||||
|
||||
/* planar YUV formats */
|
||||
I420 = 200,
|
||||
NV12,
|
||||
YV12,
|
||||
Y800,
|
||||
P010,
|
||||
|
||||
/* packed YUV formats */
|
||||
YVYU = 300,
|
||||
YUY2,
|
||||
UYVY,
|
||||
HDYC,
|
||||
|
||||
/* encoded formats */
|
||||
MJPEG = 400,
|
||||
H264,
|
||||
HEVC,
|
||||
};
|
||||
|
||||
enum class AudioFormat {
|
||||
Any,
|
||||
Unknown,
|
||||
|
||||
/* raw formats */
|
||||
Wave16bit = 100,
|
||||
WaveFloat,
|
||||
|
||||
/* encoded formats */
|
||||
AAC = 200,
|
||||
AC3,
|
||||
MPGA, /* MPEG 1 */
|
||||
};
|
||||
|
||||
enum class AudioMode {
|
||||
Capture,
|
||||
DirectSound,
|
||||
WaveOut,
|
||||
};
|
||||
|
||||
enum class Result {
|
||||
Success,
|
||||
InUse,
|
||||
Error,
|
||||
};
|
||||
|
||||
struct VideoInfo {
|
||||
int minCX, minCY;
|
||||
int maxCX, maxCY;
|
||||
int granularityCX, granularityCY;
|
||||
long long minInterval, maxInterval;
|
||||
VideoFormat format;
|
||||
};
|
||||
|
||||
struct AudioInfo {
|
||||
int minChannels, maxChannels;
|
||||
int channelsGranularity;
|
||||
int minSampleRate, maxSampleRate;
|
||||
int sampleRateGranularity;
|
||||
AudioFormat format;
|
||||
};
|
||||
|
||||
struct DeviceId {
|
||||
std::wstring name;
|
||||
std::wstring path;
|
||||
};
|
||||
|
||||
struct VideoDevice : DeviceId {
|
||||
bool audioAttached = false;
|
||||
bool separateAudioFilter = false;
|
||||
std::vector<VideoInfo> caps;
|
||||
};
|
||||
|
||||
struct AudioDevice : DeviceId {
|
||||
std::vector<AudioInfo> caps;
|
||||
};
|
||||
|
||||
struct Config : DeviceId {
|
||||
/** Use the device's desired default config */
|
||||
bool useDefaultConfig = true;
|
||||
};
|
||||
|
||||
struct VideoConfig : Config {
|
||||
VideoProc callback;
|
||||
ReactivateProc reactivateCallback;
|
||||
|
||||
/** Desired width/height of video. */
|
||||
int cx = 0, cy_abs = 0;
|
||||
|
||||
/** Whether or not cy was negative. */
|
||||
bool cy_flip = false;
|
||||
|
||||
/** Desired frame interval (in 100-nanosecond units) */
|
||||
long long frameInterval = 0;
|
||||
|
||||
/** Internal video format. */
|
||||
VideoFormat internalFormat = VideoFormat::Any;
|
||||
|
||||
/** Desired video format. */
|
||||
VideoFormat format = VideoFormat::Any;
|
||||
};
|
||||
|
||||
struct AudioConfig : Config {
|
||||
AudioProc callback;
|
||||
|
||||
/**
|
||||
* Use the audio attached to the video device
|
||||
*
|
||||
* (name/path member variables will be ignored)
|
||||
*/
|
||||
bool useVideoDevice = false;
|
||||
|
||||
/** Use separate filter for audio */
|
||||
bool useSeparateAudioFilter = false;
|
||||
|
||||
/** Desired sample rate */
|
||||
int sampleRate = 0;
|
||||
|
||||
/** Desired channels */
|
||||
int channels = 0;
|
||||
|
||||
/** Desired audio format */
|
||||
AudioFormat format = AudioFormat::Any;
|
||||
|
||||
/** Audio playback mode */
|
||||
AudioMode mode = AudioMode::Capture;
|
||||
|
||||
/** Desired buffer */
|
||||
int buffer = 0;
|
||||
};
|
||||
|
||||
class DSHOWCAPTURE_EXPORT Device {
|
||||
HDevice *context;
|
||||
|
||||
public:
|
||||
Device(InitGraph initialize = InitGraph::False);
|
||||
~Device();
|
||||
|
||||
bool Valid() const;
|
||||
|
||||
bool ResetGraph();
|
||||
void ShutdownGraph();
|
||||
|
||||
bool SetVideoConfig(VideoConfig *config);
|
||||
bool SetAudioConfig(AudioConfig *config);
|
||||
|
||||
/**
|
||||
* Connects all the configured filters together.
|
||||
*
|
||||
* Call SetVideoConfig and/or SetAudioConfig before using.
|
||||
*/
|
||||
bool ConnectFilters();
|
||||
|
||||
Result Start();
|
||||
void Stop();
|
||||
|
||||
bool GetVideoConfig(VideoConfig &config) const;
|
||||
bool GetAudioConfig(AudioConfig &config) const;
|
||||
bool GetVideoDeviceId(DeviceId &id) const;
|
||||
bool GetAudioDeviceId(DeviceId &id) const;
|
||||
|
||||
/**
|
||||
* Opens a DirectShow dialog associated with this device
|
||||
*
|
||||
* @param type The dialog type
|
||||
*/
|
||||
void OpenDialog(void *hwnd, DialogType type) const;
|
||||
|
||||
static bool EnumVideoDevices(std::vector<VideoDevice> &devices);
|
||||
static bool EnumAudioDevices(std::vector<AudioDevice> &devices);
|
||||
};
|
||||
|
||||
struct VideoEncoderConfig : DeviceId {
|
||||
int fpsNumerator;
|
||||
int fpsDenominator;
|
||||
int bitrate;
|
||||
int keyframeInterval;
|
||||
int cx;
|
||||
int cy;
|
||||
};
|
||||
|
||||
struct EncoderPacket {
|
||||
unsigned char *data;
|
||||
size_t size;
|
||||
long long pts;
|
||||
long long dts;
|
||||
};
|
||||
|
||||
class VideoEncoder {
|
||||
HVideoEncoder *context;
|
||||
|
||||
public:
|
||||
VideoEncoder();
|
||||
~VideoEncoder();
|
||||
|
||||
bool Valid() const;
|
||||
bool Active() const;
|
||||
|
||||
bool ResetGraph();
|
||||
|
||||
bool SetConfig(VideoEncoderConfig &config);
|
||||
bool GetConfig(VideoEncoderConfig &config) const;
|
||||
|
||||
bool Encode(unsigned char *data[DSHOW_MAX_PLANES],
|
||||
size_t linesize[DSHOW_MAX_PLANES], long long timestampStart,
|
||||
long long timestampEnd, EncoderPacket &packet,
|
||||
bool &new_packet);
|
||||
|
||||
static bool EnumEncoders(std::vector<DeviceId> &encoders);
|
||||
};
|
||||
|
||||
enum class LogType {
|
||||
Error,
|
||||
Warning,
|
||||
Info,
|
||||
Debug,
|
||||
};
|
||||
|
||||
typedef void (*LogCallback)(LogType type, const wchar_t *msg, void *param);
|
||||
|
||||
DSHOWCAPTURE_EXPORT void SetLogCallback(LogCallback callback, void *param);
|
||||
};
|
||||
3
obs-studio/deps/libdshowcapture/src/external/.clang-format
vendored
Normal file
3
obs-studio/deps/libdshowcapture/src/external/.clang-format
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
Language: Cpp
|
||||
SortIncludes: false
|
||||
DisableFormat: true
|
||||
13
obs-studio/deps/libdshowcapture/src/external/capture-device-support/.gitignore
vendored
Normal file
13
obs-studio/deps/libdshowcapture/src/external/capture-device-support/.gitignore
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
CMakeLists.txt.user
|
||||
CMakeCache.txt
|
||||
CMakeFiles
|
||||
CMakeScripts
|
||||
Testing
|
||||
Makefile
|
||||
cmake_install.cmake
|
||||
install_manifest.txt
|
||||
compile_commands.json
|
||||
CTestTestfile.cmake
|
||||
_deps
|
||||
.vs/
|
||||
out/
|
||||
64
obs-studio/deps/libdshowcapture/src/external/capture-device-support/CMakeLists.txt
vendored
Normal file
64
obs-studio/deps/libdshowcapture/src/external/capture-device-support/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2022 Corsair Memory, Inc.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
|
||||
# CMakeList.txt : CMake project for EGAVHIDSample, include source and define
|
||||
# project specific logic here.
|
||||
#
|
||||
cmake_minimum_required (VERSION 3.8)
|
||||
|
||||
project ("EGAVHIDSample")
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
set(FRAMEWORK_FOLDER "Library")
|
||||
|
||||
if(WIN32)
|
||||
set(PLATFORM_FOLDER "win")
|
||||
set(PLATFORM_SOURCES
|
||||
SampleCode/DriverInterface.cpp
|
||||
)
|
||||
|
||||
elseif(APPLE)
|
||||
set(PLATFORM_FOLDER "mac")
|
||||
set(PLATFORM_SOURCES)
|
||||
endif()
|
||||
|
||||
# Add source to this project's executable.
|
||||
add_executable (EGAVHIDSample
|
||||
${PLATFORM_SOURCES}
|
||||
"${FRAMEWORK_FOLDER}/EGAVResult.cpp"
|
||||
"${FRAMEWORK_FOLDER}/ElgatoUVCDevice.cpp"
|
||||
"${FRAMEWORK_FOLDER}/${PLATFORM_FOLDER}/EGAVHIDImplementation.cpp"
|
||||
"SampleCode/DriverInterface.cpp"
|
||||
"SampleCode/main.cpp"
|
||||
)
|
||||
|
||||
target_include_directories(EGAVHIDSample PRIVATE ${FRAMEWORK_FOLDER})
|
||||
target_compile_definitions(EGAVHIDSample PUBLIC EGAV_API)
|
||||
|
||||
if(WIN32)
|
||||
target_compile_definitions(EGAVHIDSample PUBLIC _UP_WINDOWS=1)
|
||||
elseif(APPLE)
|
||||
target_compile_definitions(EGAVHIDSample PUBLIC _UP_MAC=1)
|
||||
endif()
|
||||
21
obs-studio/deps/libdshowcapture/src/external/capture-device-support/LICENSE
vendored
Normal file
21
obs-studio/deps/libdshowcapture/src/external/capture-device-support/LICENSE
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Corsair Memory, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
72
obs-studio/deps/libdshowcapture/src/external/capture-device-support/Library/EGAVDevice.h
vendored
Normal file
72
obs-studio/deps/libdshowcapture/src/external/capture-device-support/Library/EGAVDevice.h
vendored
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Corsair Memory, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
|
||||
//==============================================================================
|
||||
// # Class EGAVDeviceID
|
||||
//==============================================================================
|
||||
enum class EGAVBusType { Unknown, USB, PCI };
|
||||
|
||||
struct EGAVDeviceID
|
||||
{
|
||||
EGAVDeviceID() {}
|
||||
|
||||
//! @brief EGAVDeviceID constructor
|
||||
//! @param inBusType USB or PCIe
|
||||
//! @param inVendorID USB vendor ID or PCIe subsystem vendor ID
|
||||
//! @param inProductID USB product ID or PCIe subsystem device ID
|
||||
//! @param inLocationID Location ID (macOS only)
|
||||
EGAVDeviceID(EGAVBusType inBusType, uint16_t inVendorID, uint16_t inProductID, uint32_t inLocationID = 0) :
|
||||
busType(inBusType), vendorID(inVendorID), productID(inProductID), locationID(inLocationID)
|
||||
{
|
||||
}
|
||||
|
||||
EGAVBusType busType = EGAVBusType::Unknown;
|
||||
uint16_t vendorID = 0; //!< USB vendor ID or PCI subvendor ID
|
||||
uint16_t productID = 0; //!< USB product ID or PCI subdevice ID
|
||||
uint32_t locationID = 0; //!< USB location ID (macOS only)
|
||||
|
||||
bool Equals(const EGAVDeviceID& inDeviceID, bool inIgnoreLocation) const
|
||||
{
|
||||
if (this->busType != inDeviceID.busType) return false;
|
||||
if (this->vendorID != inDeviceID.vendorID) return false;
|
||||
if (this->productID != inDeviceID.productID) return false;
|
||||
if (this->locationID != inDeviceID.locationID && !inIgnoreLocation) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator == (const EGAVDeviceID& inDeviceID) const { return Equals(inDeviceID, false); }
|
||||
bool operator != (const EGAVDeviceID& inDeviceID) const { return !Equals(inDeviceID, false); }
|
||||
|
||||
std::string toString();
|
||||
};
|
||||
|
||||
|
||||
|
||||
69
obs-studio/deps/libdshowcapture/src/external/capture-device-support/Library/EGAVHID.h
vendored
Normal file
69
obs-studio/deps/libdshowcapture/src/external/capture-device-support/Library/EGAVHID.h
vendored
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Corsair Memory, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
@file EGAVHID.h
|
||||
|
||||
@brief HID specific types and constants.
|
||||
**/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "EGAVResult.h"
|
||||
#include "EGAVDevice.h" // required for EGAVDeviceID
|
||||
|
||||
const int kHidDefaultReportID = 0; //! Dummy report ID
|
||||
|
||||
//==============================================================================
|
||||
// # Interfaces (HID)
|
||||
//==============================================================================
|
||||
|
||||
//! @brief HID Interface for cross-platform support. (EVH-442)
|
||||
class EGAVHIDInterface
|
||||
{
|
||||
public:
|
||||
virtual ~EGAVHIDInterface() { }
|
||||
|
||||
virtual EGAVResult InitHIDInterface(const EGAVDeviceID& inDeviceID) = 0;
|
||||
virtual EGAVResult DeinitHIDInterface() = 0;
|
||||
|
||||
//! @brief Reads a HID response message from the OS.
|
||||
//! @param outReceivedMessage will contain the resulting message. Its length will be adjusted automatically.
|
||||
//! @param report ID The reportID is always 0 (kHidDefaultReportID) for Facecam (Penna)
|
||||
//! @param inReadBufferSize size of buffer passed to ID read routine. If 0 mHIDCaps->InputReportByteLength is used.
|
||||
virtual EGAVResult ReadHID(std::vector<uint8_t>& outMessage, int inReportID, int inReadBufferSize = 0) = 0;
|
||||
|
||||
//! @brief Writes specified message.
|
||||
//! The implementation should construct a HID report to send to the hardware
|
||||
//! @param message (NOT report!)
|
||||
//! @param report ID The reportID is always 0 (kHidDefaultReportID) for Facecam (Penna). The Penna-specific message tag is in the first byte of the message.
|
||||
virtual EGAVResult WriteHID(const std::vector<uint8_t>& inMessage, int inReportID) = 0;
|
||||
};
|
||||
|
||||
//! @brief Platform specific factory method
|
||||
std::shared_ptr<EGAVHIDInterface> CreateEGAVHIDInterface();
|
||||
131
obs-studio/deps/libdshowcapture/src/external/capture-device-support/Library/EGAVResult.cpp
vendored
Normal file
131
obs-studio/deps/libdshowcapture/src/external/capture-device-support/Library/EGAVResult.cpp
vendored
Normal file
@ -0,0 +1,131 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Corsair Memory, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
@file EGAVResult.cpp
|
||||
|
||||
@brief Definition of error and success codes
|
||||
**/
|
||||
//==============================================================================
|
||||
|
||||
#include "EGAVResult.h"
|
||||
#if _UP_WINDOWS
|
||||
#include <winerror.h> // for HRESULT
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Constructor / Destructor
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
EGAVResult::EGAVResult(EGAVResultCode inResultCode)
|
||||
{
|
||||
mResultCode = inResultCode;
|
||||
}
|
||||
|
||||
EGAVResult::EGAVResult(EGAVResultCustomType inCustomResultType, int64_t inCustomResultCode)
|
||||
{
|
||||
mResultCode = ErrCustom;
|
||||
mCustomResultType = inCustomResultType;
|
||||
mCustomResultCode = inCustomResultCode;
|
||||
}
|
||||
|
||||
#if _UP_WINDOWS
|
||||
void EGAVResult::InitWithHresult(HRESULT hr)
|
||||
{
|
||||
mResultCode = ErrCustom;
|
||||
mCustomResultType = EGAVResultCustomType::Hresult;
|
||||
mCustomResultCode = hr;
|
||||
}
|
||||
|
||||
void EGAVResult::InitWithWinError(LONG err)
|
||||
{
|
||||
mResultCode = ErrCustom;
|
||||
mCustomResultType = EGAVResultCustomType::WinError;
|
||||
mCustomResultCode = err;
|
||||
}
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool EGAVResult::Succeeded() const
|
||||
{
|
||||
if (mResultCode == ErrCustom)
|
||||
{
|
||||
switch (mCustomResultType)
|
||||
{
|
||||
#if _UP_WINDOWS
|
||||
case EGAVResultCustomType::Hresult: return SUCCEEDED(mCustomResultCode);
|
||||
case EGAVResultCustomType::WinError: return (mCustomResultCode == ERROR_SUCCESS) ? true : false;
|
||||
#elif _UP_MAC
|
||||
case EGAVResultCustomType::Mac: return (mCustomResultCode == 0) ? true : false;
|
||||
#endif
|
||||
case EGAVResultCustomType::MainConcept:
|
||||
case EGAVResultCustomType::Device:
|
||||
return (mCustomResultCode == 0) ? true : false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return (int32_t)mResultCode > 0 ? true : false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Operators
|
||||
//------------------------------------------------------------------------------
|
||||
void EGAVResult::operator=(const EGAVResultCode inResultCode)
|
||||
{
|
||||
mCustomResultType = EGAVResultCustomType::None;
|
||||
mResultCode = inResultCode;
|
||||
}
|
||||
|
||||
bool EGAVResult::operator==(const EGAVResult inResult) const
|
||||
{
|
||||
if (mResultCode != inResult.mResultCode ) return false;
|
||||
if (mCustomResultType != inResult.mCustomResultType) return false;
|
||||
if (mCustomResultCode != inResult.mCustomResultCode) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EGAVResult::operator!=(const EGAVResult inResult) const
|
||||
{
|
||||
return !(*this == inResult);
|
||||
}
|
||||
|
||||
|
||||
bool EGAVResult::operator==(const EGAVResultCode inResultCode) const
|
||||
{
|
||||
return mResultCode == inResultCode;
|
||||
}
|
||||
|
||||
bool EGAVResult::operator!=(const EGAVResultCode inResultCode) const
|
||||
{
|
||||
return mResultCode != inResultCode;
|
||||
}
|
||||
213
obs-studio/deps/libdshowcapture/src/external/capture-device-support/Library/EGAVResult.h
vendored
Normal file
213
obs-studio/deps/libdshowcapture/src/external/capture-device-support/Library/EGAVResult.h
vendored
Normal file
@ -0,0 +1,213 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Corsair Memory, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
@file EGAVResult.h
|
||||
|
||||
@brief Definition of error and success codes
|
||||
**/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Includes
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#include <string>
|
||||
#if _UP_WINDOWS
|
||||
// FMB NOTE: For some strange reason <WinSock2.h> must be before <Windows.h>.
|
||||
// This is only necessary because contents of <WinSock2.h> are required in other code files.
|
||||
// However, if <WinSock2.h> is include somewhere it must be guaranteded that <Windows.h> was never include before.
|
||||
#define NOMINMAX
|
||||
#include <WinSock2.h> // for sockaddr_in
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
//#include "EGAVFeatures.h" // for EGAV_API
|
||||
|
||||
|
||||
#define EVH_AVOID_UNNEEDED_COPY_CONSTRUCTORS (!_UP_WINDOWS) //!< 0 - explicit copy constructors: required for .NET frontends connected via SWIG e.g. 4KCU and EVH Test App
|
||||
//!< 1 - to avoid "warning: definition of implicit copy constructor for different classes e.g.
|
||||
//! 'EGAVAudioSampleType' is deprecated because it has a user-declared copy assignment operator [-Wdeprecated-copy]"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//==============================================================================
|
||||
// EGAVResultCustomType
|
||||
//==============================================================================
|
||||
|
||||
enum class EGAVResultCustomType
|
||||
{
|
||||
None,
|
||||
Hresult, //!< HRESULT (Windows)
|
||||
WinError, //!< System error code (Windows)
|
||||
MainConcept, //!< MainConcept error codes (BS_OK etc.)
|
||||
Device, //!< Device Error Codes
|
||||
Mac //!< Errors from macOS
|
||||
};
|
||||
|
||||
enum class EGAVResultCustomTypeDevice {
|
||||
None,
|
||||
SpeedInsufficient,
|
||||
ResultUnexpected,
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
// EGAVResult
|
||||
//==============================================================================
|
||||
typedef int EGAVResultCode;
|
||||
|
||||
//! Error description
|
||||
class /*EGAV_API*/ EGAVResult
|
||||
{
|
||||
public:
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Constants
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// EXTEND_EGAVResultCode: add new error codes here and in GetResultCodeString function in cpp file
|
||||
static const EGAVResultCode ErrInvalidOperation = -300; //!< Execution of the operation would lead to an error/invalid state
|
||||
|
||||
static const EGAVResultCode ErrUnknownUnit = -200; //!< Can't instantiate desired EGAV unit (source, sink, input, output, etc.)
|
||||
|
||||
static const EGAVResultCode ErrDeviceInUse = -108; //!< Device is in use by another application (from EVHALResultCode)
|
||||
static const EGAVResultCode ErrInvalidPath = -101; //!< File error: specifided path is not valid
|
||||
static const EGAVResultCode ErrCouldNotOpenFile = -100; //!< File error: Could not open file
|
||||
|
||||
static const EGAVResultCode ErrResultPending = -19; //!< hardware busy, try again later
|
||||
static const EGAVResultCode ErrResourceNotAvail = -18; //!< Resource not available
|
||||
static const EGAVResultCode ErrOutOfRange = -17; //!< Out of Range
|
||||
static const EGAVResultCode ErrTimeOut = -16; //!< Operation timed out
|
||||
static const EGAVResultCode ErrNotSupported = -15; //!< Operation not supported; used with firmware update
|
||||
static const EGAVResultCode ErrConversionFailed = -14; //!< Conversion failed
|
||||
static const EGAVResultCode ErrNotFound = -13; //!< Not found
|
||||
static const EGAVResultCode ErrNoData = -12; //!< No data
|
||||
static const EGAVResultCode ErrVideoScaler = -11; //!< Video scaler error
|
||||
static const EGAVResultCode ErrEncoder = -10; //!< Encoder error
|
||||
static const EGAVResultCode ErrInvalidFormat = -9; //!< Invalid format
|
||||
static const EGAVResultCode ErrInvalidParameter = -8; //!< Invalid parameter
|
||||
static const EGAVResultCode ErrInvalidState = -7; //!< Invalid state (e.g. when trying to process data while a unit is desinitialized)
|
||||
static const EGAVResultCode ErrInsufficientMemory = -6; //!< Out of memory
|
||||
static const EGAVResultCode ErrNotInitialized = -5; //!< Not initialized
|
||||
static const EGAVResultCode ErrInvalidCast = -4; //!< Cast operation failed
|
||||
static const EGAVResultCode ErrNotImplemented = -3; //!< Can't instantiate desired EGAV unit (source, sink, input, output, etc.)
|
||||
static const EGAVResultCode ErrNullPointer = -2; //!< Null pointer
|
||||
static const EGAVResultCode ErrUnknown = -1; //!< General failure
|
||||
|
||||
static const EGAVResultCode ErrCustom = 0; //!< Custom error code: error code is in mCustomResultCode
|
||||
|
||||
static const EGAVResultCode Ok = 1; //!< Success
|
||||
static const EGAVResultCode OkNoDataChanged = 2; //!< Success: No data were changed (similar to HRESULT value S_FALSE )
|
||||
static const EGAVResultCode OkFileNotFound = 3; //!< Success: File was not found, but that is a valid state.
|
||||
static const EGAVResultCode OkButIncomplete = 4; //!< Success: Operation didn't fail, but had some internal uncritical errors (similar to S_FALSE on Windows)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Construction
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//! Constructor
|
||||
EGAVResult() {}
|
||||
EGAVResult(EGAVResultCode inResultCode);
|
||||
|
||||
EGAVResult(EGAVResultCustomType inCustomResultType, int64_t inCustomResultCode);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Initialization
|
||||
//------------------------------------------------------------------------------
|
||||
#if _UP_WINDOWS
|
||||
|
||||
//! Init with HRESULT (EGAVResultCustomType::Hresult)
|
||||
void InitWithHresult(HRESULT hr);
|
||||
//! Init with Windows error code (EGAVResultCustomType::WinError)
|
||||
void InitWithWinError(LONG err);
|
||||
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool Succeeded() const;
|
||||
bool Failed() const { return !Succeeded(); }
|
||||
|
||||
EGAVResultCode GetResultCode() const { return mResultCode; }
|
||||
EGAVResultCustomType GetCustomResultType() const { return mCustomResultType; }
|
||||
int64_t GetCustomResultCode() const { return mCustomResultCode; }
|
||||
|
||||
|
||||
void operator=(const EGAVResultCode inResultCode);
|
||||
bool operator==(const EGAVResult inResult) const;
|
||||
bool operator!=(const EGAVResult inResult) const;
|
||||
bool operator==(const EGAVResultCode inResultCode) const;
|
||||
bool operator!=(const EGAVResultCode inResultCode) const;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Members
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Common
|
||||
EGAVResultCode mResultCode = ErrCustom;
|
||||
|
||||
// Custom error codes (platform error codes or error codes from other APIs)
|
||||
EGAVResultCustomType mCustomResultType = EGAVResultCustomType::None;
|
||||
int64_t mCustomResultCode = 0;
|
||||
|
||||
private:
|
||||
std::string mMessage;
|
||||
};
|
||||
|
||||
|
||||
|
||||
//==============================================================================
|
||||
// # Macros
|
||||
//==============================================================================
|
||||
|
||||
#define EGAVResult_CheckPointer(_p_) \
|
||||
{ \
|
||||
if (!_p_) \
|
||||
return EGAVResult::ErrNullPointer; \
|
||||
}
|
||||
|
||||
#define EGAVResult_CheckCondition(_cond_) \
|
||||
{ \
|
||||
if (false == (_cond_)) \
|
||||
return EGAVResult::ErrUnknown; \
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifndef EGAV_OVERRIDE_DEBUG_MACROS
|
||||
inline void dummy() {}
|
||||
|
||||
#define EPL_ASSERT_BREAK(...) dummy()
|
||||
#define error_printf(...) dummy()
|
||||
#define warning_printf(...) dummy()
|
||||
#define info_printf(...) dummy()
|
||||
#endif
|
||||
265
obs-studio/deps/libdshowcapture/src/external/capture-device-support/Library/ElgatoUVCDevice.cpp
vendored
Normal file
265
obs-studio/deps/libdshowcapture/src/external/capture-device-support/Library/ElgatoUVCDevice.cpp
vendored
Normal file
@ -0,0 +1,265 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022-23 Corsair Memory, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "ElgatoUVCDevice.h"
|
||||
|
||||
|
||||
#define WORKAROUND_HD60_S_PLUS_PAYLOAD_SIZE 1 //!< Workaround HD60 S+ firmware issue: invalid payload length (seen with HDR and SPD info frames)
|
||||
|
||||
|
||||
//==============================================================================
|
||||
// # Elgato HID interface for UVC devices
|
||||
//==============================================================================
|
||||
|
||||
|
||||
enum class I2CAddress
|
||||
{
|
||||
MCU = 0x55,
|
||||
};
|
||||
|
||||
//! @brief I2C registers for MCU (I2C address 0x55).
|
||||
enum class MCU_I2C_REGISTER
|
||||
{
|
||||
GET_HDR_PACKET = 0x09, //!< HDR capable devices (HD60 S+, HD60 X)
|
||||
XET_HDR_TONEMAPPING = 0x0A, //!< HDR capable devices (HD60 S+, HD60 X): Enable hardware tonemapping; param 0/1 (uint8_t)
|
||||
};
|
||||
|
||||
|
||||
|
||||
//==============================================================================
|
||||
// ## HID interface - I2C
|
||||
//==============================================================================
|
||||
|
||||
//! @brief HID report case for new device type.
|
||||
enum class REPORT_CASE_NEW
|
||||
{
|
||||
REPORT_IIC_WRITE = 6,
|
||||
REPORT_IIC_READ = 7
|
||||
};
|
||||
|
||||
//! @brief HID report IDs for new device type. Can also be queried via HidP_GetValueCaps()
|
||||
enum class HID_REPORT_ID_NEW
|
||||
{
|
||||
I2C_READ = 5,
|
||||
I2C_WRITE = 6
|
||||
};
|
||||
|
||||
//! @brief HDI report IDs for original device type. Can also be queried via HidP_GetValueCaps()
|
||||
enum class HID_REPORT_ID
|
||||
{
|
||||
I2C_READ_SET_ID = 9,
|
||||
I2C_READ_GET_ID = 10,
|
||||
I2C_WRITE_ID = 11
|
||||
};
|
||||
|
||||
const int I2C_BUFFER_HEADER_SIZE = 4;
|
||||
const int MAX_COMM_READ_BUFFER_SIZE = 32;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
// # Helpers
|
||||
//==============================================================================
|
||||
|
||||
std::vector<EGAVDeviceID> GetElgatoUVCDeviceIDs()
|
||||
{
|
||||
std::vector<EGAVDeviceID> devices {deviceIDHD60SPlus, deviceIDHD60X, deviceIDHD60XRev2 };
|
||||
// EXTEND_DEVICES
|
||||
return devices;
|
||||
}
|
||||
|
||||
//! @return true for new USB chipset
|
||||
bool IsNewDeviceType(const EGAVDeviceID& inDeviceID) { return (inDeviceID != deviceIDHD60SPlus); }
|
||||
|
||||
|
||||
|
||||
//==============================================================================
|
||||
// # Class ElgatoUVCDevice
|
||||
//==============================================================================
|
||||
|
||||
ElgatoUVCDevice::ElgatoUVCDevice(std::shared_ptr<EGAVHIDInterface> hid, bool isNewDeviceType) : mHIDImpl(hid), mNewDeviceType(isNewDeviceType) {}
|
||||
|
||||
EGAVResult ElgatoUVCDevice::ReadI2cData(uint8_t inI2CAddress, uint8_t inRegister, uint8_t* outData, uint8_t inLength)
|
||||
{
|
||||
EGAVResult_CheckPointer(outData);
|
||||
EGAVResult_CheckPointer(mHIDImpl);
|
||||
|
||||
EPL_ASSERT_BREAK(inLength <= MAX_COMM_READ_BUFFER_SIZE);
|
||||
|
||||
const std::lock_guard<std::recursive_mutex> lock(mHIDMutex);
|
||||
|
||||
EGAVResult res = EGAVResult::ErrUnknown;
|
||||
|
||||
if (mNewDeviceType)
|
||||
{
|
||||
const uint8_t writeLen = 1 /* +1 for byte register address*/, readLen = inLength, reportLen = 4 + writeLen + sizeof(readLen);
|
||||
std::vector<uint8_t> outputMessage{ reportLen, (uint8_t)REPORT_CASE_NEW::REPORT_IIC_READ, inI2CAddress, writeLen, inRegister, readLen };
|
||||
EPL_ASSERT_BREAK(reportLen == outputMessage.size());
|
||||
res = mHIDImpl->WriteHID(outputMessage, (int)HID_REPORT_ID_NEW::I2C_WRITE);
|
||||
if (res.Failed())
|
||||
error_printf("WriteHID() FAILED for I2C address 0x%02x, register 0x%02x", inI2CAddress, inRegister);
|
||||
else
|
||||
{
|
||||
std::vector<uint8_t> inputMessage;
|
||||
const int inputReportLength = 0xFF | ((int)REPORT_CASE_NEW::REPORT_IIC_READ << 8); // report case is coded into report length
|
||||
res = mHIDImpl->ReadHID(inputMessage, (int)HID_REPORT_ID_NEW::I2C_READ, inputReportLength);
|
||||
if (res.Failed())
|
||||
error_printf("ReadHID() FAILED for I2C address 0x%02x, register 0x%02x", inI2CAddress, inRegister);
|
||||
else
|
||||
{
|
||||
int dataLen = std::min((int)inLength, (int)(inputMessage.size() - 1));
|
||||
memcpy(outData, inputMessage.data() + 1, dataLen);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<uint8_t> outputMessage{ inI2CAddress, inRegister, inLength };
|
||||
res = mHIDImpl->WriteHID(outputMessage, (int)HID_REPORT_ID::I2C_READ_SET_ID);
|
||||
if (res.Failed())
|
||||
error_printf("WriteHID() FAILED for I2C address 0x%02x, register 0x%02x", inI2CAddress, inRegister);
|
||||
else
|
||||
{
|
||||
std::vector<uint8_t> inputMessage(I2C_BUFFER_HEADER_SIZE + MAX_COMM_READ_BUFFER_SIZE);
|
||||
res = mHIDImpl->ReadHID(inputMessage, (int)HID_REPORT_ID::I2C_READ_GET_ID);
|
||||
if (res.Failed())
|
||||
error_printf("ReadHID() FAILED for I2C address 0x%02x, register 0x%02x", inI2CAddress, inRegister);
|
||||
else
|
||||
memcpy(outData, inputMessage.data(), inLength);
|
||||
}
|
||||
}
|
||||
|
||||
EPL_ASSERT_BREAK(res.Succeeded());
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
EGAVResult ElgatoUVCDevice::WriteI2cData(uint8_t inI2CAddress, uint8_t inRegister, uint8_t* inData, uint8_t inLength)
|
||||
{
|
||||
EGAVResult_CheckPointer(inData);
|
||||
EGAVResult_CheckPointer(mHIDImpl);
|
||||
|
||||
const std::lock_guard<std::recursive_mutex> lock(mHIDMutex);
|
||||
|
||||
EGAVResult res = EGAVResult::ErrUnknown;
|
||||
|
||||
if (mNewDeviceType)
|
||||
{
|
||||
const uint8_t writeLen = 1 + inLength /* +1 for byte register address*/, reportLen = 4 + writeLen;
|
||||
std::vector<uint8_t> outputMessage{ reportLen, (uint8_t)REPORT_CASE_NEW::REPORT_IIC_WRITE, inI2CAddress, writeLen, inRegister };
|
||||
for (int i = 0; i < inLength; i++)
|
||||
outputMessage.push_back(inData[i]);
|
||||
EPL_ASSERT_BREAK(reportLen == outputMessage.size());
|
||||
|
||||
res = mHIDImpl->WriteHID(outputMessage, (int)HID_REPORT_ID_NEW::I2C_WRITE);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<uint8_t> outputMessage{ inI2CAddress, inRegister, inLength };
|
||||
for (int i = 0; i < inLength; i++)
|
||||
outputMessage.push_back(inData[i]);
|
||||
res = mHIDImpl->WriteHID(outputMessage, (int)HID_REPORT_ID::I2C_WRITE_ID);
|
||||
}
|
||||
|
||||
if (res.Failed())
|
||||
error_printf("WriteHID() FAILED for I2C address 0x%02d, register 0x%02d", inI2CAddress, inRegister);
|
||||
return res;
|
||||
}
|
||||
|
||||
void ElgatoUVCDevice::SetHDRTonemappingEnabled(bool inValue)
|
||||
{
|
||||
const std::lock_guard<std::recursive_mutex> lock(mHIDMutex);
|
||||
|
||||
uint8_t buffer = inValue ? 1 : 0;
|
||||
WriteI2cData((uint8_t)I2CAddress::MCU, (uint8_t)MCU_I2C_REGISTER::XET_HDR_TONEMAPPING, &buffer, sizeof(buffer));
|
||||
}
|
||||
|
||||
EGAVResult ElgatoUVCDevice::GetHDMIHDRStatusPacket(HDMI_GENERIC_INFOFRAME& outFrame)
|
||||
{
|
||||
const std::lock_guard<std::recursive_mutex> lock(mHIDMutex);
|
||||
|
||||
const size_t bufSize = mNewDeviceType ? 32 : 33;
|
||||
uint8_t* buffer = new uint8_t[bufSize];
|
||||
EGAVResult res = ReadI2cData((uint8_t)I2CAddress::MCU, (uint8_t)MCU_I2C_REGISTER::GET_HDR_PACKET, buffer, (uint8_t)bufSize);
|
||||
if (res.Succeeded())
|
||||
{
|
||||
size_t size = (bufSize < sizeof(outFrame)) ? bufSize : sizeof(outFrame);
|
||||
memcpy(&outFrame, mNewDeviceType ? buffer : buffer+1, size);
|
||||
|
||||
#if WORKAROUND_HD60_S_PLUS_PAYLOAD_SIZE
|
||||
// Workaround HD60 S+ firmware issue: invalid payload length (seen with HDR and SPD info frames)
|
||||
// Also with HD60 X FW 22.03.24 (MCU: 22.03.16)
|
||||
if (outFrame.header.bPayloadLength > HDMI_MAX_INFOFRAME_PAYLOAD)
|
||||
{
|
||||
if (HDMI_INFOFRAME_TYPE_DR == outFrame.header.bfType)
|
||||
{
|
||||
int diff = outFrame.header.bPayloadLength - sizeof(outFrame.plDR1);
|
||||
outFrame.header.bPayloadLength = sizeof(outFrame.plDR1);
|
||||
outFrame.bChecksum += diff;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
delete [] buffer;
|
||||
return res;
|
||||
}
|
||||
|
||||
EGAVResult ElgatoUVCDevice::IsVideoHDR(bool& outIsHDR)
|
||||
{
|
||||
// Try to read HDR meta data
|
||||
HDMI_GENERIC_INFOFRAME frame{}, emptyFrame{};
|
||||
memset(&emptyFrame, 0, sizeof(emptyFrame));
|
||||
EGAVResult res = GetHDMIHDRStatusPacket(frame);
|
||||
if (res.Succeeded())
|
||||
{
|
||||
bool isInfoFrameValid = HDMI_IsInfoFrameValid(&frame);
|
||||
res = isInfoFrameValid ? EGAVResult::Ok : EGAVResult::ErrUnknown;
|
||||
if (isInfoFrameValid)
|
||||
{
|
||||
// Check type in header and EOTF flag in payload
|
||||
if (HDMI_INFOFRAME_TYPE_DR == frame.header.bfType && HDMI_DR_EOTF_SDRGAMMA != frame.plDR1.bfEOTF)
|
||||
{
|
||||
outIsHDR = true;
|
||||
}
|
||||
else if (HDMI_INFOFRAME_TYPE_DR == frame.header.bfType && HDMI_DR_EOTF_SDRGAMMA == frame.plDR1.bfEOTF)
|
||||
{
|
||||
outIsHDR = false; // we get here with HD60 X (22.03.24 (MCU: 22.03.16))
|
||||
}
|
||||
else if (0 /*HDMI_INFOFRAME_TYPE_RESERVED*/ == frame.header.bfType && (0 == memcmp(&frame, &emptyFrame, sizeof(emptyFrame))))
|
||||
{
|
||||
// all empty (seen with HD60 S+ when HDR is not active)
|
||||
outIsHDR = false;
|
||||
}
|
||||
else if (HDMI_INFOFRAME_TYPE_DR != frame.header.bfType)
|
||||
{
|
||||
warning_printf("HDMI Metadata: Wrong header type: %d", frame.header.bfType);
|
||||
res = EGAVResult::ErrNotFound;
|
||||
}
|
||||
}
|
||||
else
|
||||
warning_printf("HDMI Metadata: HDMI_IsInfoFrameValid() returned error (checksum)!");
|
||||
}
|
||||
else
|
||||
warning_printf("HDMI Metadata: GetHDMIHDRStatusPacket() failed!");
|
||||
return res;
|
||||
}
|
||||
84
obs-studio/deps/libdshowcapture/src/external/capture-device-support/Library/ElgatoUVCDevice.h
vendored
Normal file
84
obs-studio/deps/libdshowcapture/src/external/capture-device-support/Library/ElgatoUVCDevice.h
vendored
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022-23 Corsair Memory, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "EGAVResult.h"
|
||||
#include "EGAVDevice.h"
|
||||
#include "HDMIInfoFramesAPI.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include "win/EGAVHIDImplementation.h"
|
||||
#else
|
||||
#include "mac/EGAVHIDImplementation.h"
|
||||
#endif
|
||||
|
||||
// Supported devices
|
||||
inline const EGAVDeviceID deviceIDHD60SPlus (EGAVBusType::USB, 0x0FD9, 0x006A); //!< HD60 S+
|
||||
inline const EGAVDeviceID deviceIDHD60X (EGAVBusType::USB, 0x0FD9, 0x0082); //!< HD60 X
|
||||
inline const EGAVDeviceID deviceIDHD60XRev2 (EGAVBusType::USB, 0x0FD9, 0x008A); //!< HD60 X Rev. 2
|
||||
// EXTEND_DEVICES
|
||||
|
||||
|
||||
//! @return Device IDs of supported Elgato UVC devices
|
||||
std::vector<EGAVDeviceID> GetElgatoUVCDeviceIDs();
|
||||
|
||||
|
||||
//! @return true for new devices with new USB chipset
|
||||
bool IsNewDeviceType(const EGAVDeviceID& inDeviceID);
|
||||
|
||||
|
||||
|
||||
//==============================================================================
|
||||
// # Class ElgatoUVCDevice
|
||||
//==============================================================================
|
||||
|
||||
class ElgatoUVCDevice
|
||||
{
|
||||
|
||||
public:
|
||||
ElgatoUVCDevice(std::shared_ptr<EGAVHIDInterface> hid, bool isNewDeviceType);
|
||||
|
||||
//! @brief Works with HD60 S+, HD60 X or newer
|
||||
void SetHDRTonemappingEnabled(bool inEnable);
|
||||
|
||||
//! @brief Works with HD60 S+, HD60 X or newer
|
||||
EGAVResult GetHDMIHDRStatusPacket(HDMI_GENERIC_INFOFRAME& outFrame);
|
||||
|
||||
//! @brief Works with HD60 S+, HD60 X or newer
|
||||
EGAVResult IsVideoHDR(bool& outIsHDR);
|
||||
|
||||
|
||||
private:
|
||||
EGAVResult WriteI2cData(uint8_t inI2CAddress, uint8_t inRegister, uint8_t* inData, uint8_t inLength);
|
||||
EGAVResult ReadI2cData(uint8_t inI2CAddress, uint8_t inRegister, uint8_t* outData, uint8_t inLength);
|
||||
|
||||
bool mNewDeviceType = false; //!< true: HD60 X and newer devices , false: HD60 S+
|
||||
|
||||
std::shared_ptr<EGAVHIDInterface> mHIDImpl;
|
||||
std::recursive_mutex mHIDMutex;
|
||||
};
|
||||
746
obs-studio/deps/libdshowcapture/src/external/capture-device-support/Library/HDMIInfoFramesAPI.h
vendored
Normal file
746
obs-studio/deps/libdshowcapture/src/external/capture-device-support/Library/HDMIInfoFramesAPI.h
vendored
Normal file
@ -0,0 +1,746 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Corsair Memory, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
|
||||
//====================================================================================
|
||||
// # VIDEO IDENTIFICATION CODES (VIC)
|
||||
//====================================================================================
|
||||
|
||||
// Video ID Code, see CEA-861-E chapter 4.1, table 4
|
||||
// Video ID Code, see CEA-861-G chapter 4.1, table 3
|
||||
|
||||
typedef struct _HDMI_VIC_DESCRIPTOR
|
||||
{
|
||||
uint8_t bID; //!< Video ID Code
|
||||
int iWidth; //!< width in pixels
|
||||
int iHeight; //!< height in pixels
|
||||
int iFieldRate; //!< field refresh rate in Hz
|
||||
bool bInterlaced; //!< interlaced
|
||||
short iAspectX; //!< picture aspect ratio H
|
||||
short iAspectY; //!< picture aspect ratio V
|
||||
}
|
||||
HDMI_VIC_DESCRIPTOR;
|
||||
|
||||
// a field refresh value of 24Hz means either 24.00Hz or 23.98Hz
|
||||
// a field refresh value of 30Hz means either 30.00Hz or 29.97Hz
|
||||
// a field refresh value of 48Hz means either 48.00Hz or 47.95Hz
|
||||
// a field refresh value of 60Hz means either 60.00Hz or 59.94Hz
|
||||
// a field refresh value of 120Hz means either 120.00Hz or 119.88Hz
|
||||
// a field refresh value of 240Hz means either 240.00Hz or 239.76Hz
|
||||
|
||||
#define HDMI_VIC_TABLE_SIZE 220
|
||||
|
||||
extern const HDMI_VIC_DESCRIPTOR g_HDMI_VIC_TABLE[HDMI_VIC_TABLE_SIZE];
|
||||
|
||||
//====================================================================================
|
||||
// # INFO FRAME TYPES
|
||||
//====================================================================================
|
||||
|
||||
|
||||
// see CEA-861-E chapter 6, table 6
|
||||
// see CEA-861-G chapter 6, table 5
|
||||
|
||||
#define HDMI_INFOFRAME_TYPE_RESERVED 0x00 // reserved
|
||||
|
||||
#define HDMI_INFOFRAME_TYPE_VS 0x01 // Vendor Specific
|
||||
#define HDMI_INFOFRAME_TYPE_AVI 0x02 // Auxiliary Video Information
|
||||
#define HDMI_INFOFRAME_TYPE_SPD 0x03 // Source Product Description
|
||||
#define HDMI_INFOFRAME_TYPE_A 0x04 // Audio
|
||||
#define HDMI_INFOFRAME_TYPE_MS 0x05 // MPEG Source
|
||||
#define HDMI_INFOFRAME_TYPE_VBI 0x06 // NTSC VBI
|
||||
#define HDMI_INFOFRAME_TYPE_DR 0x07 // Dynamic Range and Mastering
|
||||
|
||||
#define HDMI_INFOFRAME_TYPE_MIN HDMI_INFOFRAME_TYPE_VS
|
||||
#define HDMI_INFOFRAME_TYPE_MAX HDMI_INFOFRAME_TYPE_DR
|
||||
|
||||
|
||||
//====================================================================================
|
||||
// # INFO FRAME HEADER
|
||||
//====================================================================================
|
||||
|
||||
// see CEA-861-G Annex D.1
|
||||
|
||||
#define HDMI_MAX_INFOFRAME_SIZE 31 // 3 bytes header + 1 byte checksum + 27 bytes payload
|
||||
#define HDMI_MAX_INFOFRAME_PAYLOAD 27
|
||||
|
||||
|
||||
// see CEA-861-E chapter 6
|
||||
|
||||
typedef struct _HDMI_INFOFRAMEHEADER
|
||||
{
|
||||
uint8_t bfType : 7; // InfoFrame Type Code (see HDMI_INFOFRAME_TYPE_*)
|
||||
uint8_t bfPacketType : 1; // the HDMI Packet Type is 0x80+InfoframeType for HDMI InfoFrame Packets
|
||||
uint8_t bfVersion : 7; // InfoFrame Version Number, starting with 1
|
||||
uint8_t bfChangeBit : 1; // InfoFrame Change Bit, VS Infoframe only
|
||||
uint8_t bPayloadLength; // Size of InfoFrame payload, not including Type, Version, Length
|
||||
}
|
||||
HDMI_INFOFRAMEHEADER;
|
||||
|
||||
|
||||
//====================================================================================
|
||||
// # VENDOR SPECIFIC INFO FRAME
|
||||
//====================================================================================
|
||||
|
||||
// see CEA-861-E chapter 6.1, table 7
|
||||
// see CEA-861-G chapter 6.1, table 6
|
||||
// type code is 0x01, version is 0x01, size is vendor specific
|
||||
|
||||
typedef struct _HDMI_VS1_PAYLOAD
|
||||
{
|
||||
uint8_t IEEERegistrationID[3]; // IEEE OUI
|
||||
uint8_t bVendorSpecificPayload[HDMI_MAX_INFOFRAME_SIZE - 3 - 3 - 1]; // 24 bytes
|
||||
}
|
||||
HDMI_VS1_PAYLOAD;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
// see CEA-861-G chapter 6.1, table 7
|
||||
// type code is 0x01, version is 0x02, size is vendor specific
|
||||
// VS infoframe version 2 uses bit 7 of the version number as the ChangeBit
|
||||
|
||||
typedef struct _HDMI_VS2_PAYLOAD
|
||||
{
|
||||
uint8_t IEEERegistrationID[3]; // IEEE OUI
|
||||
uint8_t bVendorSpecificPayload[HDMI_MAX_INFOFRAME_SIZE - 3 - 3 - 1]; // 24 bytes
|
||||
}
|
||||
HDMI_VS2_PAYLOAD;
|
||||
|
||||
|
||||
//====================================================================================
|
||||
// # AUXILIARY VIDEO INFORMATION INFOFRAME
|
||||
//====================================================================================
|
||||
|
||||
// Scan Information, see CEA-861-E chapter 6.4, table 10
|
||||
#define HDMI_AVI_S_NODATA 0x00 // no data
|
||||
#define HDMI_AVI_S_OVERSCAN 0x01 // composed for overscan
|
||||
#define HDMI_AVI_S_UNDERSCAN 0x02 // composed for underscan
|
||||
|
||||
// Bar Data Present, see CEA-861-E chapter 6.4, table 10
|
||||
#define HDMI_AVI_B_NODATA 0x00 // no data
|
||||
#define HDMI_AVI_B_V 0x01 // vertical bar info present
|
||||
#define HDMI_AVI_B_H 0x02 // horizontal bar info present
|
||||
#define HDMI_AVI_B_VH 0x03 // vertical and horizontal bar info present
|
||||
|
||||
// Active Format Information Present, see CEA-861-E chapter 6.4, table 10
|
||||
#define HDMI_AVI_A_NONE 0x00 // no information
|
||||
#define HDMI_AVI_A_PRESENT 0x01 // information present
|
||||
|
||||
// RGB or YCbCr, see CEA-861-E chapter 6.4, table 10
|
||||
// RGB or YCbCr, see CEA-861-G chapter 6.4, table 10
|
||||
#define HDMI_AVI_Y_RGB 0x00 // RGB
|
||||
#define HDMI_AVI_Y_YCBCR422 0x01 // YCbCr 4:2:2
|
||||
#define HDMI_AVI_Y_YCBCR444 0x02 // YCbCr 4:4:4
|
||||
#define HDMI_AVI_Y_YCBCR420 0x03 // YCbCr 4:2:0
|
||||
#define HDMI_AVI_Y_IDO 0x07 // IDO-Defined
|
||||
|
||||
// Active Portion Aspect Ration, see CEA-861-E chapter 6.4, table 11
|
||||
#define HDMI_AVI_R_SAME 0x08 // same as coded frame aspect ration
|
||||
#define HDMI_AVI_R_4TO3 0x09 // 4:3 (center)
|
||||
#define HDMI_AVI_R_16TO9 0x0A // 16:9 (center)
|
||||
#define HDMI_AVI_R_14TO9 0x0B // 14:9 (center)
|
||||
|
||||
// Coded Frame Aspect Ration, see CEA-861-E chapter 6.4, table 11
|
||||
#define HDMI_AVI_M_NODATA 0x00 // no data
|
||||
#define HDMI_AVI_M_4TO3 0x01 // 4:3
|
||||
#define HDMI_AVI_M_16TO9 0x02 // 16:9
|
||||
|
||||
// Colorimetry, see CEA-861-E chapter 6.4, table 11
|
||||
#define HDMI_AVI_C_NODATA 0x00 // no data
|
||||
#define HDMI_AVI_C_SMTPE170M 0x01 // SMTPE 170M
|
||||
#define HDMI_AVI_C_ITUR709 0x02 // ITU-R 709
|
||||
#define HDMI_AVI_C_EXTENDED 0x03 // extended colorimetry information valid
|
||||
|
||||
// Non-Uniform Picture Scaling, see CEA-861-E chapter 6.4, table 13
|
||||
#define HDMI_AVI_SC_NO 0x00 // no known scaling
|
||||
#define HDMI_AVI_SC_H 0x01 // picture has been scaled horizontally
|
||||
#define HDMI_AVI_SC_V 0x02 // picture has been scaled vertically
|
||||
#define HDMI_AVI_SC_HV 0x03 // picture has been scaled horizontally and vertically
|
||||
|
||||
// RGB Quantization Range, see CEA-861-E chapter 6.4, table 13
|
||||
#define HDMI_AVI_Q_DEFAULT 0x00 // default, depends on video format
|
||||
#define HDMI_AVI_Q_LIMITED 0x01 // limited range
|
||||
#define HDMI_AVI_Q_FULL 0x02 // full range
|
||||
|
||||
// Extended Colorimetry, see CEA-861-E chapter 6.4, table 13
|
||||
// Extended Colorimetry, see CEA-861-G chapter 6.4, table 13
|
||||
#define HDMI_AVI_EC_XVYCC601 0x00 // xvYCC 601
|
||||
#define HDMI_AVI_EC_XVYCC709 0x01 // xvYCC 709
|
||||
#define HDMI_AVI_EC_SYCC601 0x02 // sYCC 601
|
||||
#define HDMI_AVI_EC_ADOBEYCC601 0x03 // Adobe YCC 601
|
||||
#define HDMI_AVI_EC_ADOBERGB 0x04 // Adobe RGB
|
||||
#define HDMI_AVI_EC_BT2020C 0x05 // ITU BT2020 YcCbcCrc
|
||||
#define HDMI_AVI_EC_BT2020 0x06 // ITU BT2020 RGB or YCbCr
|
||||
#define HDMI_AVI_EC_EXTENDED 0x07 // extended information, see HDMI_AVI_ACE_*
|
||||
|
||||
// IT Content, see CEA-861-E chapter 6.4, table 13
|
||||
#define HDMI_AVI_ITC_NODATA 0x00 // no data
|
||||
#define HDMI_AVI_ITC_VALID 0x01 // IT Content, CN is valid
|
||||
|
||||
// Pixel Repetition Factor, see CEA-861-E chapter 6.4, table 15
|
||||
#define HDMI_AVI_PR_NONE 0x00 // pixels are not repeated, i.e. only sent once in total
|
||||
#define HDMI_AVI_PR_1 0x01 // pixels are repeated once, i.e. sent twice in total
|
||||
#define HDMI_AVI_PR_2 0x02 // pixels are repeated twice, i.e. sent three times in total
|
||||
#define HDMI_AVI_PR_3 0x03 // pixels are repeated three times, i.e. sent 4 times in total
|
||||
#define HDMI_AVI_PR_4 0x04 // pixels are repeated 4 times, i.e. sent 5 times in total
|
||||
#define HDMI_AVI_PR_5 0x05 // pixels are repeated 5 times, i.e. sent 6 times in total
|
||||
#define HDMI_AVI_PR_6 0x06 // pixels are repeated 6 times, i.e. sent 7 times in total
|
||||
#define HDMI_AVI_PR_7 0x07 // pixels are repeated 7 times, i.e. sent 8 times in total
|
||||
#define HDMI_AVI_PR_8 0x08 // pixels are repeated 8 times, i.e. sent 9 times in total
|
||||
#define HDMI_AVI_PR_9 0x09 // pixels are repeated 9 times, i.e. sent 10 times in total
|
||||
|
||||
// IT Content Type, see CEA-861-E chapter 6.4, table 16
|
||||
#define HDMI_AVI_CN_GRAPHICS 0x00 // graphics
|
||||
#define HDMI_AVI_CN_PHOTO 0x01 // photo
|
||||
#define HDMI_AVI_CN_CINEMA 0x02 // cinema
|
||||
#define HDMI_AVI_CN_GAME 0x03 // game
|
||||
|
||||
// YCC Quantization Range, see CEA-861-E chapter 6.4, table 17
|
||||
#define HDMI_AVI_YQ_LIMITED 0x00 // limited range
|
||||
#define HDMI_AVI_YQ_FULL 0x01 // full range
|
||||
|
||||
// Additional Colorimetry Extension, see CEA-861-G chapter 6.4, table 25
|
||||
#define HDMI_AVI_ACE_DCIP3D65 0x00 // DCI-P3 R'G'B' (D65)
|
||||
#define HDMI_AVI_ACE_DCIP3TH 0x01 // DCI-P3 R'G'B' (Theater)
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
// see CEA-861-E chapter 6.3, table 8
|
||||
// type code is 0x02, version is 0x01, size is 13
|
||||
// ATTENTION: this AVI version is obsolete and should not be used!
|
||||
|
||||
typedef struct _HDMI_AVI1_PAYLOAD
|
||||
{
|
||||
// data byte 1, see CEA-861-E chapter 6.4, table 10
|
||||
uint8_t bfScanInformation : 2; // S0, S1
|
||||
uint8_t bfBarDataPresent : 2; // B0, B1
|
||||
uint8_t bfActiveFormatInformationPresent : 1; // A0
|
||||
uint8_t bfRGBorYCbCr : 2; // Y0, Y1
|
||||
uint8_t bfFutureUse1 : 1; // reserved, zero
|
||||
|
||||
// data byte 2, see CEA-861-E chapter 6.4, table 11
|
||||
uint8_t bfActivePortionAspectRatio : 4; // R0, R1, R2, R3
|
||||
uint8_t bfCodedFrameAspectRatio : 2; // M0, M1
|
||||
uint8_t bfColorimetry : 2; // C0, C1
|
||||
|
||||
// data byte 3, see CEA-861-E chapter 6.4, table 13
|
||||
uint8_t bfNonUniformPictureScaling : 2; // SC0, SC1
|
||||
uint8_t bfFutureUse3 : 6; // reserved, zero
|
||||
|
||||
uint8_t bfFutureUse4 = 0; // reserved, zero
|
||||
uint8_t bfFutureUse5 = 0; // reserved, zero
|
||||
|
||||
uint16_t wLineNumberOfEndOfTopBar; // ETB
|
||||
uint16_t wLineNumberOfStartOfBottomBar; // SBB
|
||||
uint16_t wPixelNumberOfEndOfLeftBar; // ELB
|
||||
uint16_t wPixelNumberOfStartOfRightBar; // SRB
|
||||
}
|
||||
HDMI_AVI1_PAYLOAD;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
// see CEA-861-E chapter 6.4, table 9
|
||||
// see CEA-861-E chapter 6.4, table 8
|
||||
// type code is 0x02, version is 0x02, size is 13
|
||||
|
||||
typedef struct _HDMI_AVI2_PAYLOAD
|
||||
{
|
||||
// data byte 1, see CEA-861-E chapter 6.4, table 10
|
||||
uint8_t bfScanInformation : 2; // S0, S1
|
||||
uint8_t bfBarDataPresent : 2; // B0, B1
|
||||
uint8_t bfActiveFormatInformationPresent : 1; // A0
|
||||
uint8_t bfRGBorYCbCr : 2; // Y0, Y1
|
||||
uint8_t bfFutureUse1 : 1; // reserved, zero
|
||||
|
||||
// data byte 2, see CEA-861-E chapter 6.4, table 11
|
||||
uint8_t bfActivePortionAspectRatio : 4; // R0, R1, R2, R3
|
||||
uint8_t bfCodedFrameAspectRatio : 2; // M0, M1
|
||||
uint8_t bfColorimetry : 2; // C0, C1
|
||||
|
||||
// data byte 3, see CEA-861-E chapter 6.4, table 13
|
||||
uint8_t bfNonUniformPictureScaling : 2; // SC0, SC1
|
||||
uint8_t bfRGBQuantizationRange : 2; // Q0, Q1
|
||||
uint8_t bfExtendedColorimetry : 3; // EC0, EC1, EC2
|
||||
uint8_t bfITContent : 1; // ITC
|
||||
|
||||
// data byte 4, see CEA-861-E chapter 4.1, table 4
|
||||
uint8_t bfVIC : 7; // VIC
|
||||
uint8_t bfFutureUse4 : 1; // reserved, zero
|
||||
|
||||
// data byte 5, see CEA-861-E chapter 6.4, tables 15, 16, 17
|
||||
uint8_t bfPixelRepetitionFactor : 4; // PR0, PR1, PR2, PR3
|
||||
uint8_t bfITContentType : 2; // CN0, CN1
|
||||
uint8_t bfYCCQuantizationRange : 2; // YQ0, YQ1
|
||||
|
||||
uint16_t wLineNumberOfEndOfTopBar; // ETB
|
||||
uint16_t wLineNumberOfStartOfBottomBar; // SBB
|
||||
uint16_t wPixelNumberOfEndOfLeftBar; // ELB
|
||||
uint16_t wPixelNumberOfStartOfRightBar; // SRB
|
||||
}
|
||||
HDMI_AVI2_PAYLOAD;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
// see CEA-861-E chapter 6.4, table 8
|
||||
// type code is 0x02, version is 0x03, size is 13
|
||||
|
||||
typedef struct _HDMI_AVI3_PAYLOAD
|
||||
{
|
||||
// data byte 1, see CEA-861-G chapter 6.4, table 10
|
||||
uint8_t bfScanInformation : 2; // S0, S1
|
||||
uint8_t bfBarDataPresent : 2; // B0, B1
|
||||
uint8_t bfActiveFormatInformationPresent : 1; // A0
|
||||
uint8_t bfRGBorYCbCr : 3; // Y0, Y1, Y2
|
||||
|
||||
// data byte 2, see CEA-861-E chapter 6.4, table 11
|
||||
uint8_t bfActivePortionAspectRatio : 4; // R0, R1, R2, R3
|
||||
uint8_t bfCodedFrameAspectRatio : 2; // M0, M1
|
||||
uint8_t bfColorimetry : 2; // C0, C1
|
||||
|
||||
// data byte 3, see CEA-861-E chapter 6.4, table 13
|
||||
uint8_t bfNonUniformPictureScaling : 2; // SC0, SC1
|
||||
uint8_t bfRGBQuantizationRange : 2; // Q0, Q1
|
||||
uint8_t bfExtendedColorimetry : 3; // EC0, EC1, EC2
|
||||
uint8_t bfITContent : 1; // ITC
|
||||
|
||||
// data byte 4, see CEA-861-E chapter 4.1, table 4
|
||||
uint8_t bVIC; // VIC
|
||||
|
||||
// data byte 5, see CEA-861-E chapter 6.4, tables 15, 16, 17
|
||||
uint8_t bfPixelRepetitionFactor : 4; // PR0, PR1, PR2, PR3
|
||||
uint8_t bfITContentType : 2; // CN0, CN1
|
||||
uint8_t bfYCCQuantizationRange : 2; // YQ0, YQ1
|
||||
|
||||
uint16_t wLineNumberOfEndOfTopBar; // ETB
|
||||
uint16_t wLineNumberOfStartOfBottomBar; // SBB
|
||||
uint16_t wPixelNumberOfEndOfLeftBar; // ELB
|
||||
uint16_t wPixelNumberOfStartOfRightBar; // SRB
|
||||
}
|
||||
HDMI_AVI3_PAYLOAD;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
// see CEA-861-E chapter 6.4, table 9
|
||||
// type code is 0x02, version is 0x04, size is 14
|
||||
|
||||
typedef struct _HDMI_AVI4_PAYLOAD
|
||||
{
|
||||
// data byte 1, see CEA-861-G chapter 6.4, table 10
|
||||
uint8_t bfScanInformation : 2; // S0, S1
|
||||
uint8_t bfBarDataPresent : 2; // B0, B1
|
||||
uint8_t bfActiveFormatInformationPresent : 1; // A0
|
||||
uint8_t bfRGBorYCbCr : 3; // Y0, Y1, Y2
|
||||
|
||||
// data byte 2, see CEA-861-E chapter 6.4, table 11
|
||||
uint8_t bfActivePortionAspectRatio : 4; // R0, R1, R2, R3
|
||||
uint8_t bfCodedFrameAspectRatio : 2; // M0, M1
|
||||
uint8_t bfColorimetry : 2; // C0, C1
|
||||
|
||||
// data byte 3, see CEA-861-E chapter 6.4, table 13
|
||||
uint8_t bfNonUniformPictureScaling : 2; // SC0, SC1
|
||||
uint8_t bfRGBQuantizationRange : 2; // Q0, Q1
|
||||
uint8_t bfExtendedColorimetry : 3; // EC0, EC1, EC2
|
||||
uint8_t bfITContent : 1; // ITC
|
||||
|
||||
// data byte 4, see CEA-861-E chapter 4.1, table 4
|
||||
uint8_t bVIC; // VIC
|
||||
|
||||
// data byte 5, see CEA-861-E chapter 6.4, tables 15, 16, 17
|
||||
uint8_t bfPixelRepetitionFactor : 4; // PR0, PR1, PR2, PR3
|
||||
uint8_t bfITContentType : 2; // CN0, CN1
|
||||
uint8_t bfYCCQuantizationRange : 2; // YQ0, YQ1
|
||||
|
||||
uint16_t wLineNumberOfEndOfTopBar; // ETB
|
||||
uint16_t wLineNumberOfStartOfBottomBar; // SBB
|
||||
uint16_t wPixelNumberOfEndOfLeftBar; // ELB
|
||||
uint16_t wPixelNumberOfStartOfRightBar; // SRB
|
||||
|
||||
// data byte 14, see CEA-861-G chapter 6.4, tables 15, 16, 17
|
||||
uint8_t bfReserved14 : 4; // reserved, zero
|
||||
uint8_t bfAdditionalColorimetry : 4; // ACE
|
||||
}
|
||||
HDMI_AVI4_PAYLOAD;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#define HDMI_ERROR -1
|
||||
|
||||
#define HDMI_UNKNOWN 0
|
||||
|
||||
#define HDMI_FORMAT_RGB 1 // RGB
|
||||
#define HDMI_FORMAT_YCBCR420 2 // YCbCr 4:2:0
|
||||
#define HDMI_FORMAT_YCBCR422 3 // YCbCr 4:2:2
|
||||
#define HDMI_FORMAT_YCBCR444 4 // YCbCr 4:4:4
|
||||
|
||||
#define HDMI_COLOR_ADOBERGB 1
|
||||
#define HDMI_COLOR_BT2020 2
|
||||
#define HDMI_COLOR_DCIP3D65 3
|
||||
#define HDMI_COLOR_DCIP3TH 4
|
||||
#define HDMI_COLOR_SMPTE170M 5
|
||||
#define HDMI_COLOR_BT709 6
|
||||
#define HDMI_COLOR_XVYCC601 7
|
||||
#define HDMI_COLOR_XVYCC709 8
|
||||
#define HDMI_COLOR_SYCC601 9
|
||||
#define HDMI_COLOR_ADOBEYCC601 10
|
||||
#define HDMI_COLOR_BT2020C 11
|
||||
|
||||
|
||||
//====================================================================================
|
||||
// # SOURCE PRODUCT DESCRIPTION INFOFRAME (SPD)
|
||||
//====================================================================================
|
||||
|
||||
|
||||
// Source Information, see CEA-861-E chapter 6.5, table 22
|
||||
#define HDMI_SPD_SI_UNKNOWN 0x00
|
||||
#define HDMI_SPD_SI_STB 0x01
|
||||
#define HDMI_SPD_SI_DVD 0x02
|
||||
#define HDMI_SPD_SI_DVHS 0x03
|
||||
#define HDMI_SPD_SI_DVR 0x04
|
||||
#define HDMI_SPD_SI_DVC 0x05
|
||||
#define HDMI_SPD_SI_DSC 0x06
|
||||
#define HDMI_SPD_SI_VCD 0x07
|
||||
#define HDMI_SPD_SI_GAME 0x08
|
||||
#define HDMI_SPD_SI_PC 0x09
|
||||
#define HDMI_SPD_SI_BD 0x0A
|
||||
#define HDMI_SPD_SI_SACD 0x0B
|
||||
#define HDMI_SPD_SI_HDDVD 0x0C
|
||||
#define HDMI_SPD_SI_PMP 0x0D
|
||||
|
||||
const char* HDMI_SPD_ToString(uint8_t inByte);
|
||||
|
||||
//! @brief Maps abbreviated names to user-friendly names (e.g. MSFT --> Microsoft)
|
||||
//! @param inManufacturer manufacturer string as contained in HDMI info packet
|
||||
std::string HDMI_SPD_MapManufacturerString(const std::string& inManufacturer);
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
// see CEA-861-E chapter 6.5, table 21
|
||||
// type code is 0x03, version is 1, size is 25
|
||||
|
||||
typedef struct _HDMI_SPD1_PAYLOAD
|
||||
{
|
||||
uint8_t bVendorName[8];
|
||||
uint8_t bProductDescription[16];
|
||||
uint8_t bSourceInformation;
|
||||
}
|
||||
HDMI_SPD1_PAYLOAD;
|
||||
|
||||
|
||||
//====================================================================================
|
||||
// AUDIO INFOFRAME
|
||||
//====================================================================================
|
||||
|
||||
|
||||
// Audio Channel Count, see CEA-861-E chapter 6.6.1, table 24
|
||||
#define HDMI_A_CC_STREAM 0x00 // see stream header
|
||||
|
||||
// Audio Coding Type, see CEA-861-E chapter 6.6.1, table 24
|
||||
#define HDMI_A_CT_STREAM 0x00 // see stream header
|
||||
#define HDMI_A_CT_PCM 0x01 // PCM
|
||||
#define HDMI_A_CT_AC3 0x02 // AC-3
|
||||
#define HDMI_A_CT_MPEG1 0x03 // MPEG-1
|
||||
#define HDMI_A_CT_MP3 0x04 // MP3
|
||||
#define HDMI_A_CT_MPEG2 0x05 // MPEG-2
|
||||
#define HDMI_A_CT_AACLC 0x06 // AAC-LC
|
||||
#define HDMI_A_CT_DTS 0x07 // DTS
|
||||
#define HDMI_A_CT_ATRAC 0x08 // ATRAC
|
||||
#define HDMI_A_CT_DSD 0x09 // DSD
|
||||
#define HDMI_A_CT_EAC3 0x0A // E-AC-3
|
||||
#define HDMI_A_CT_DTSHD 0x0B // DTS-HD
|
||||
#define HDMI_A_CT_MLP 0x0C // MLP
|
||||
#define HDMI_A_CT_DST 0x0D // DST
|
||||
#define HDMI_A_CT_WMAPRO 0x0E // WMA Pro
|
||||
#define HDMI_A_CT_CXT 0x0F // see Audio Coding Extension Type (CXT)
|
||||
|
||||
// Sample Size, see CEA-861-E chapter 6.6.1, table 25
|
||||
#define HDMI_A_SS_STREAM 0x00 // see stream header
|
||||
#define HDMI_A_SS_16BIT 0x01 // 16bit
|
||||
#define HDMI_A_SS_20BIT 0x02 // 20bit
|
||||
#define HDMI_A_SS_24BIT 0x03 // 24bit
|
||||
|
||||
// Sample Frequency, see CEA-861-E chapter 6.6.1, table 25
|
||||
#define HDMI_A_SF_STREAM 0x00 // see stream header
|
||||
#define HDMI_A_SF_32000 0x01 // 32kHz
|
||||
#define HDMI_A_SF_44100 0x02 // 44.1kHz
|
||||
#define HDMI_A_SF_48000 0x03 // 48kHz
|
||||
#define HDMI_A_SF_88200 0x04 // 88.2kHz
|
||||
#define HDMI_A_SF_96000 0x05 // 96kHz
|
||||
#define HDMI_A_SF_176400 0x06 // 176.4kHz
|
||||
#define HDMI_A_SF_192000 0x07 // 192kHz
|
||||
|
||||
// Audio Coding Extension Type, see CEA-861-E chapter 6.6.1, table 26
|
||||
#define HDMI_A_CXT_CT 0x00 // see Audio Coding Type (CT)
|
||||
#define HDMI_A_CXT_HEAAC 0x01 // HE-AAC
|
||||
#define HDMI_A_CXT_HEAAC2 0x02 // HE-AAC v2
|
||||
#define HDMI_A_CXT_MPEGSURROUND 0x03 // MPEG Surround
|
||||
|
||||
// Down-mix Inhibit, see CEA-861-E chapter 6.6.2, table 30
|
||||
#define HDMI_A_DM_PERMITTED 0x00 // down mix permitted
|
||||
#define HDMI_A_DM_PROHIBITED 0x01 // down mix prohibited
|
||||
|
||||
// LFE Playback Level, see CEA-861-E chapter 6.6.2, table 31
|
||||
#define HDMI_A_LFEPBL_UNKNOWN 0x00 // unknown
|
||||
#define HDMI_A_LFEPBL_0DB 0x01 // 0 dB
|
||||
#define HDMI_A_LFEPBL_10DB 0x02 // +10 dB
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
// see CEA-861-E chapter 6.6, table 23
|
||||
// type code is 0x04, version is 1, size is 10
|
||||
|
||||
typedef struct _HDMI_A1_PAYLOAD
|
||||
{
|
||||
// data byte 1, see CEA-861-E chapter 6.6.1, table 24
|
||||
uint8_t bfChannelCount : 3; // CC0, CC1, CC2 (channel count - 1)
|
||||
uint8_t bfReserved1 : 1; // reserved, zero
|
||||
uint8_t bfAudioCodingType : 4; // CT0, CT1, CT2, CT3
|
||||
|
||||
// data byte 2, see CEA-861-E chapter 6.6.1, table 25
|
||||
uint8_t bfSampleSize : 2; // SS0, SS1
|
||||
uint8_t bfSampleFrequency : 3; // SF0, SF1, SF2
|
||||
uint8_t bfReserved2 : 3; // reserved, zero
|
||||
|
||||
// data byte 3, see CEA-861-E chapter 6.6.1, table 26
|
||||
uint8_t bfAudioCodingExtensionType : 5; // CXT0, CXT1, CXT2, CXT3, CXT4
|
||||
uint8_t bfReserved3 : 3; // reserved, zero
|
||||
|
||||
// data byte 4, see CEA-861-E chapter 6.6.2, table 28
|
||||
uint8_t bChannelAllocation; // CA (channel to speaker)
|
||||
|
||||
// data byte 5, see CEA-861-E chapter 6.6.2, tables 29, 30, 31
|
||||
uint8_t bfLFEPlaybackLevel : 2; // LFEPBL0, LFEPBL1
|
||||
uint8_t bfReserved5 : 1; // reserved, zero
|
||||
uint8_t bfLevelShiftValue : 4; // LSV0, LSV1 (dB)
|
||||
uint8_t bfDownMixInhibitFlag : 1; // DM_INH
|
||||
|
||||
uint8_t bReserved6 = 0; // reserved, or Speaker Mask, or Channel Index
|
||||
uint8_t bReserved7 = 0; // reserved, or Speaker Mask, or Channel Index
|
||||
uint8_t bReserved8 = 0; // reserved, or Speaker Mask, or Channel Index
|
||||
uint8_t bReserved9 = 0; // reserved, or Speaker Mask, or Channel Index
|
||||
uint8_t bReserved10 = 0; // reserved, zero
|
||||
}
|
||||
HDMI_A1_PAYLOAD;
|
||||
|
||||
|
||||
//====================================================================================
|
||||
// MPEG SOURCE INFOFRAME
|
||||
//====================================================================================
|
||||
|
||||
|
||||
// MPEG Frame, see CEA-861-E chapter 6.7, table 33
|
||||
#define HDMI_MS_MF_UNKNOWN 0x00 // unknown
|
||||
#define HDMI_MS_MF_I 0x01 // I-Frame
|
||||
#define HDMI_MS_MF_P 0x02 // P-Frame
|
||||
#define HDMI_MS_MF_B 0x03 // B-Frame
|
||||
|
||||
// Field Repeat, see CEA-861-E chapter 6.7, table 33
|
||||
#define HDMI_MS_FR_NEW 0x00 // new field
|
||||
#define HDMI_MS_FR_REPEATED 0x01 // repeated field
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
// see CEA-861-E chapter 6.7, table 32
|
||||
// type code is 0x05, version is 1, size is 10
|
||||
// ATTENTION: it is recommended not to use this info frame
|
||||
|
||||
typedef struct _HDMI_MS1_PAYLOAD
|
||||
{
|
||||
uint32_t dwMPEGBitRate; // MPEG bit rate in Hz
|
||||
|
||||
// data byte 5, see CEA-861-E chapter 6.7, table 33
|
||||
uint8_t bfMPEGFrame : 2; // MF0, MF1
|
||||
uint8_t bfReserved5a : 2; // reserved, zero
|
||||
uint8_t bfFieldRepeat : 1; // FR0
|
||||
uint8_t bfReserved5b : 3; // reserved, zero
|
||||
|
||||
uint8_t bReserved6 = 0; // reserved
|
||||
uint8_t bReserved7 = 0; // reserved
|
||||
uint8_t bReserved8 = 0; // reserved
|
||||
uint8_t bReserved9 = 0; // reserved
|
||||
uint8_t bReserved10 = 0; // reserved
|
||||
}
|
||||
HDMI_MS1_PAYLOAD;
|
||||
|
||||
|
||||
//====================================================================================
|
||||
// NTSC VBI INFOFRAME
|
||||
//====================================================================================
|
||||
|
||||
|
||||
// see CEA-861-E chapter 6.8, table 34
|
||||
// type code is 0x06, version is 1, size depends
|
||||
|
||||
typedef struct _HDMI_VBI1_PAYLOAD
|
||||
{
|
||||
uint8_t bPESDataField[HDMI_MAX_INFOFRAME_SIZE - 3 - 1]; // PES data field, limited to max 27 bytes
|
||||
}
|
||||
HDMI_VBI1_PAYLOAD;
|
||||
|
||||
|
||||
//====================================================================================
|
||||
// # DYNAMIC RANGE AND MASTERING INFOFRAME
|
||||
//====================================================================================
|
||||
|
||||
/*
|
||||
Example for valid DR info frame:
|
||||
|
||||
87 (type) | Header
|
||||
01 (version) |
|
||||
1A (length) |
|
||||
8D (checksum)
|
||||
02 00 FA 00 AE 02 85 00 | Payload
|
||||
29 00 A3 02 5C 01 40 01 |
|
||||
51 01 DB 05 00 00 DB 05 |
|
||||
1F 03 |
|
||||
*/
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
// EOTF, see CEA-861.3-A chapter 3.2, table 3
|
||||
#define HDMI_DR_EOTF_SDRGAMMA 0x00 // traditional gamma, SDR
|
||||
#define HDMI_DR_EOTF_HDRGAMMA 0x01 // traditional gamma, HDR
|
||||
#define HDMI_DR_EOTF_ST2084 0x02 // ST2084 PQ
|
||||
#define HDMI_DR_EOTF_HLG 0x03 // BT2100 HLG
|
||||
|
||||
// Metadata, see CEA-861.3-A chapter 3.2, table 4
|
||||
#define HDMI_DR_MD_STATIC 0x00 // static metadata type 1
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
// used for static metadata, see CEA-861.3-A chapter 3.2.1
|
||||
typedef struct _HDMI_XY
|
||||
{
|
||||
uint16_t X; // encoded in units of 0.00002
|
||||
uint16_t Y; // encoded in units of 0.00002
|
||||
}
|
||||
HDMI_XY;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
|
||||
//! HDR Meta Data
|
||||
//! type code is 0x07, version is 1, size depends (30 for static metadata type 1)
|
||||
//! @sa CEA-861.3-A chapter 3.2, table 2
|
||||
|
||||
typedef struct _HDMI_DR1_PAYLOAD
|
||||
{
|
||||
// data byte 1, see CEA-861.3-A chapter 3.2, table 3
|
||||
uint8_t bfEOTF : 3; // EOTF
|
||||
uint8_t bfReserved1 : 5; // reserved
|
||||
|
||||
// data byte 2, see CEA-861.3-A chapter 3.2, table 4
|
||||
uint8_t bfMetadataID : 3; // static metadata descriptor ID
|
||||
uint8_t bfReserved2 : 5; // reserved
|
||||
|
||||
// data bytes 3-22, for static metadata type 1, see CEA-861.3-A chapter 3.2.1, table 5
|
||||
HDMI_XY xyDisplayPrimaries[3]; // chromaticity of red or green or blue (ST2086)
|
||||
HDMI_XY xyWhitePoint; // white point (ST2086)
|
||||
uint16_t wMaxDisplayLuminance; // maximum display mastering luminance (ST2086), nit
|
||||
uint16_t wMinDisplayLuminance; // minimum display mastering luminance (ST2086), 0.0001 nit
|
||||
uint16_t wMaxCLL; // maximum content light level, nit
|
||||
uint16_t wMaxFALL; // maximum frame-average light level, nit
|
||||
}
|
||||
HDMI_DR1_PAYLOAD;
|
||||
|
||||
|
||||
//====================================================================================
|
||||
// GENERIC INFOFRAME TYPE
|
||||
//====================================================================================
|
||||
|
||||
|
||||
typedef struct _HDMI_GENERIC_INFOFRAME
|
||||
{
|
||||
HDMI_INFOFRAMEHEADER header; // type, version, length
|
||||
uint8_t bChecksum; // the sum of all bytes in the info frame must be zero
|
||||
|
||||
union
|
||||
{
|
||||
// generic byte array, to address the InfoFrame by index
|
||||
uint8_t bPayload[HDMI_MAX_INFOFRAME_SIZE - 3 - 1] = { 0 };
|
||||
|
||||
// specific infoframes
|
||||
HDMI_VS1_PAYLOAD plVS1;
|
||||
HDMI_VS2_PAYLOAD plVS2;
|
||||
HDMI_AVI1_PAYLOAD plAVI1;
|
||||
HDMI_AVI2_PAYLOAD plAVI2;
|
||||
HDMI_AVI3_PAYLOAD plAVI3;
|
||||
HDMI_AVI4_PAYLOAD plAVI4;
|
||||
HDMI_SPD1_PAYLOAD plSPD1;
|
||||
HDMI_A1_PAYLOAD plA1;
|
||||
HDMI_MS1_PAYLOAD plMS1;
|
||||
HDMI_VBI1_PAYLOAD plVBI1;
|
||||
HDMI_DR1_PAYLOAD plDR1;
|
||||
};
|
||||
}
|
||||
HDMI_GENERIC_INFOFRAME;
|
||||
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
|
||||
//====================================================================================
|
||||
// # FUNCTIONS
|
||||
//====================================================================================
|
||||
|
||||
// Verifies the checksum of the HDMI Info Frame
|
||||
inline bool HDMI_IsInfoFrameValid(const _HDMI_GENERIC_INFOFRAME* pInfoFrame)
|
||||
{
|
||||
if (pInfoFrame == NULL) return false;
|
||||
|
||||
unsigned char* data = (unsigned char*)pInfoFrame;
|
||||
int size = sizeof(HDMI_INFOFRAMEHEADER) + 1 + pInfoFrame->header.bPayloadLength;
|
||||
|
||||
unsigned char checksum = 0;
|
||||
while (size-- != 0) checksum += *data++;
|
||||
|
||||
return (checksum == 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,284 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Corsair Memory, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
@file EGAVHIDImplementation.cpp
|
||||
|
||||
@brief macOS implementation of EGAVHIDInterface
|
||||
**/
|
||||
//==============================================================================
|
||||
|
||||
#include "EGAVHIDImplementation.h"
|
||||
#include <IOKit/IOKitLib.h>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// PREPROCESSOR SWITCHES
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
std::shared_ptr<EGAVHIDInterface> CreateEGAVHIDInterface()
|
||||
{
|
||||
return std::make_shared<EGAVHID>();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// # HID Device Enumeration
|
||||
//==============================================================================
|
||||
|
||||
static int LocationIDOfHIDDevice(IOHIDDeviceRef hidRef)
|
||||
{
|
||||
int locationID = 0;
|
||||
|
||||
// this block is just here to see if we can find the corresponding device
|
||||
|
||||
CFTypeRef uniqueID = IOHIDDeviceGetProperty(hidRef, CFSTR(kIOHIDUniqueIDKey));
|
||||
if (CFGetTypeID(uniqueID) == CFNumberGetTypeID())
|
||||
{
|
||||
uint64_t uniqueID64 = 0;
|
||||
CFNumberGetValue((CFNumberRef)uniqueID, kCFNumberLongLongType, &uniqueID64);
|
||||
//NSLog(@"uniqueID %lld or 0x%16llx", uniqueID64, uniqueID64);
|
||||
|
||||
CFMutableDictionaryRef matchingDict = IORegistryEntryIDMatching(uniqueID64);
|
||||
|
||||
// next call consumes a reference to matchingDict
|
||||
io_service_t matchedService = IOServiceGetMatchingService(kIOMasterPortDefault, matchingDict);
|
||||
if (matchedService != 0)
|
||||
{
|
||||
CFNumberRef loc = (CFNumberRef)IORegistryEntrySearchCFProperty(matchedService, kIOServicePlane, CFSTR("locationID"), kCFAllocatorDefault, kIORegistryIterateRecursively | kIORegistryIterateParents);
|
||||
if (loc != NULL)
|
||||
{
|
||||
CFNumberGetValue(loc, kCFNumberIntType, &locationID);
|
||||
CFRelease(loc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return locationID;
|
||||
}
|
||||
|
||||
static void HIDDeviceMatchingCallback(void* inContext,
|
||||
IOReturn /*inResult*/,
|
||||
void* /*inSender*/,
|
||||
IOHIDDeviceRef inIOHIDDeviceRef)
|
||||
{
|
||||
reinterpret_cast<EGAVHID*>(inContext)->DeviceAdded(inIOHIDDeviceRef);
|
||||
}
|
||||
|
||||
static void HIDDeviceRemovalCallback(void* inContext,
|
||||
IOReturn /*inResult*/,
|
||||
void* /*inSender*/,
|
||||
IOHIDDeviceRef inIOHIDDeviceRef)
|
||||
{
|
||||
reinterpret_cast<EGAVHID*>(inContext)->DeviceRemoved(inIOHIDDeviceRef);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//==============================================================================
|
||||
// # Class EGAVHID
|
||||
//==============================================================================
|
||||
|
||||
EGAVHID::EGAVHID()
|
||||
{
|
||||
}
|
||||
|
||||
void EGAVHID::DeviceAdded(IOHIDDeviceRef deviceRef)
|
||||
{
|
||||
if (mLocationID == 0) // we don't care about a specific locationID
|
||||
{
|
||||
mHIDDevice = deviceRef;
|
||||
info_printf("## DeviceAdded()");
|
||||
}
|
||||
else if (LocationIDOfHIDDevice(deviceRef) == mLocationID)
|
||||
{
|
||||
mHIDDevice = deviceRef;
|
||||
info_printf("## DeviceAdded(): Location ID %d", mLocationID);
|
||||
}
|
||||
}
|
||||
|
||||
void EGAVHID::DeviceRemoved(IOHIDDeviceRef deviceRef)
|
||||
{
|
||||
if (mHIDDevice == deviceRef)
|
||||
{
|
||||
info_printf("## DeviceRemoved()");
|
||||
mHIDDevice = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
// ## HID interface
|
||||
//==============================================================================
|
||||
|
||||
EGAVResult EGAVHID::InitHIDInterface(const EGAVDeviceID& inDeviceID, EGAVUnitPtr /*inOwner*/, bool /* inIgnoreDevicePathCheck = false */)
|
||||
{
|
||||
dbgFunctionI();
|
||||
EGAVResult res = EGAVResult::Ok;
|
||||
|
||||
mLocationID = inDeviceID.locationID;
|
||||
|
||||
if (!mWorkerCreated) // program dies if thread is assigned when it isn't already null
|
||||
{
|
||||
mWorkerCreated = true;
|
||||
mWorker = std::thread([this, inDeviceID]
|
||||
{
|
||||
mRunLoop = CFRunLoopGetCurrent();
|
||||
|
||||
IOHIDManagerRef manager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDManagerOptionNone);
|
||||
|
||||
IOHIDManagerRegisterDeviceMatchingCallback(manager, HIDDeviceMatchingCallback, this);
|
||||
IOHIDManagerRegisterDeviceRemovalCallback(manager, HIDDeviceRemovalCallback, this);
|
||||
|
||||
CFMutableDictionaryRef matchingDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
||||
|
||||
CFNumberRef vendor = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &inDeviceID.vendorID);
|
||||
CFNumberRef product = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &inDeviceID.productID);
|
||||
|
||||
CFDictionaryAddValue(matchingDict, CFSTR(kIOHIDVendorIDKey), vendor);
|
||||
CFDictionaryAddValue(matchingDict, CFSTR(kIOHIDProductIDKey), product);
|
||||
|
||||
IOHIDManagerSetDeviceMatching(manager, matchingDict);
|
||||
|
||||
IOHIDManagerScheduleWithRunLoop(manager, mRunLoop, kCFRunLoopDefaultMode);
|
||||
|
||||
IOReturn ret = IOHIDManagerOpen(manager, kIOHIDOptionsTypeNone);
|
||||
EPL_ASSERT_BREAK(ret == kIOReturnSuccess);
|
||||
|
||||
CFRunLoopRun();
|
||||
|
||||
IOHIDManagerClose(manager, kIOHIDOptionsTypeNone);
|
||||
|
||||
CFRelease(manager);
|
||||
});
|
||||
}
|
||||
|
||||
// Wait for device found
|
||||
int64_t startTimeMsec = EplTime_GetMonotonicMilliseconds();
|
||||
const int64_t kHidDiscoveryTimeoutMsec = 1500;
|
||||
while (!mHIDDevice && EplTime_GetMonotonicMilliseconds() - startTimeMsec < kHidDiscoveryTimeoutMsec)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
|
||||
if (mHIDDevice)
|
||||
{
|
||||
// Query input report size //! @todo do this only once
|
||||
CFIndex reportSize = 0;
|
||||
CFTypeRef number = IOHIDDeviceGetProperty(mHIDDevice, CFSTR(kIOHIDMaxInputReportSizeKey));
|
||||
CFNumberGetValue((CFNumberRef)number, kCFNumberCFIndexType, &reportSize);
|
||||
mInputReportSize = (int)reportSize;
|
||||
|
||||
number = IOHIDDeviceGetProperty(mHIDDevice, CFSTR(kIOHIDMaxOutputReportSizeKey));
|
||||
CFNumberGetValue((CFNumberRef)number, kCFNumberCFIndexType, &reportSize);
|
||||
mOutputReportSize = (int)reportSize;
|
||||
}
|
||||
|
||||
return mHIDDevice ? EGAVResult::Ok : EGAVResult::ErrNotFound;
|
||||
}
|
||||
|
||||
EGAVResult EGAVHID::DeinitHIDInterface()
|
||||
{
|
||||
dbgFunctionI();
|
||||
mHIDDevice = nullptr;
|
||||
if (mRunLoop != nullptr)
|
||||
{
|
||||
CFRunLoopStop(mRunLoop);
|
||||
mWorker.join();
|
||||
mRunLoop = nullptr;
|
||||
}
|
||||
mWorkerCreated = false;
|
||||
return EGAVResult::Ok;
|
||||
}
|
||||
|
||||
EGAVResult EGAVHID::ReadHID(std::vector<uint8_t>& outMessage, int inReportId, int inReadBufferSize /*= 0*/)
|
||||
{
|
||||
EGAVResult_CheckPointer(mHIDDevice);
|
||||
|
||||
EGAVResult res = EGAVResult::Ok;
|
||||
|
||||
std::vector<uint8_t> report(mInputReportSize); // from the hardware, no zero prepended
|
||||
report[0] = inReportId;
|
||||
|
||||
int usedSize = 0;
|
||||
|
||||
{
|
||||
CFIndex bufferSize = inReadBufferSize > 0 ? inReadBufferSize : report.size();
|
||||
IOReturn err = IOHIDDeviceGetReport(mHIDDevice, kIOHIDReportTypeInput, inReportId,
|
||||
&report[0], &bufferSize);
|
||||
if (err == noErr)
|
||||
usedSize = (int)bufferSize;
|
||||
else error_printf("IOHIDDeviceGetReport() failed with IOReturn %d (0x%08X)", err, err);
|
||||
res = (err == noErr) ? EGAVResult::Ok : EGAVResult::ErrUnknown;
|
||||
}
|
||||
|
||||
if (res.Succeeded())
|
||||
{
|
||||
outMessage.clear();
|
||||
|
||||
// Facecam: calling code expects the report ID (0) in front of the report (Facecam)
|
||||
//! @todo check if this behavior is really necessary. Check on Windows also.
|
||||
if (inReportId == kHidDefaultReportID)
|
||||
outMessage.push_back(inReportId);
|
||||
|
||||
for (int i = 0; i< usedSize; i++)
|
||||
outMessage.push_back(report[i]);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
//! If device only has one report ID, it is zero (kHidDefaultReportID)
|
||||
EGAVResult EGAVHID::WriteHID(const std::vector<uint8_t>& inMessage, int inReportID)
|
||||
{
|
||||
// dbgFunctionI();
|
||||
|
||||
EGAVResult_CheckPointer(mHIDDevice);
|
||||
|
||||
std::vector<uint8_t> report;
|
||||
|
||||
// From Device Class Definition for Human Interface Devices (HID) Version 1.11
|
||||
// If a device has multiple report structures, all data transfers start with a 1-byte identifier prefix that indicates which report structure
|
||||
// applies to the transfer. This allows the class driver to distinguish incoming pointer data from keyboard data by examining the transfer prefix.
|
||||
if (inReportID != kHidDefaultReportID)
|
||||
report.push_back(inReportID);
|
||||
|
||||
for (auto m : inMessage)
|
||||
report.push_back(m);
|
||||
|
||||
report.resize(mInputReportSize, 0); // pad report with zeros, ensure it is always the right length
|
||||
IOReturn err = IOHIDDeviceSetReport(mHIDDevice, kIOHIDReportTypeOutput, inReportID,
|
||||
&report[0], report.size());
|
||||
|
||||
// 0xE00002D6 - kIOReturnTimeout
|
||||
// 0xE00002EB - kIOReturnAborted
|
||||
if (err != noErr)
|
||||
error_printf("IOHIDDeviceSetReport() failed with IOReturn %d (0x%08X)", err, err);
|
||||
|
||||
EGAVResult res = (err == noErr) ? EGAVResult::Ok : EGAVResult::ErrUnknown;
|
||||
|
||||
return res;
|
||||
}
|
||||
@ -0,0 +1,76 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Corsair Memory, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
@file EGAVHIDImplementation.h
|
||||
|
||||
@brief macOS implementation of EGAVHIDInterface
|
||||
**/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
// macOS
|
||||
#include <IOKit/hid/IOHIDManager.h>
|
||||
|
||||
#include "EGAVEngine/EGAVHID.h"
|
||||
|
||||
class HIDTransport;
|
||||
|
||||
class EGAVHID : public EGAVHIDInterface
|
||||
{
|
||||
public:
|
||||
EGAVHID();
|
||||
|
||||
void DeviceAdded(IOHIDDeviceRef deviceRef);
|
||||
void DeviceRemoved(IOHIDDeviceRef deviceRef);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ## EGAVHIDInterface implementation
|
||||
//-----------------------------------------------------------------------------
|
||||
virtual EGAVResult InitHIDInterface(const EGAVDeviceID& inDeviceID, EGAVUnitPtr inOwner, bool inIgnoreDevicePathCheck = false) override;
|
||||
virtual EGAVResult DeinitHIDInterface() override;
|
||||
|
||||
//! @brief Reads a HID response message from the OS.
|
||||
//! @param outMessage will contain the resulting message. Its length will be adjusted automatically.
|
||||
virtual EGAVResult ReadHID(std::vector<uint8_t>& outMessage, int inReportID, int inReadBufferSize = 0) override;
|
||||
|
||||
//! @brief Writes a HID report (with ID 0) containing inMessage to the OS.
|
||||
//! @param inMessage the report contents, not including the report ID.
|
||||
virtual EGAVResult WriteHID(const std::vector<uint8_t>& inMessage, int inReportID) override;
|
||||
|
||||
private:
|
||||
int mLocationID = 0;
|
||||
int mInputReportSize = 0, mOutputReportSize = 0;
|
||||
|
||||
std::atomic<IOHIDDeviceRef> mHIDDevice = nullptr;
|
||||
|
||||
std::atomic<CFRunLoopRef> mRunLoop = nullptr;
|
||||
std::thread mWorker; //!< background worker for HID device discovery
|
||||
bool mWorkerCreated = false;
|
||||
};
|
||||
@ -0,0 +1,216 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Corsair Memory, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
@file EGAVHIDImplementation.cpp
|
||||
|
||||
@brief Windows implementation of EGAVHIDInterface
|
||||
**/
|
||||
//==============================================================================
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows-hardware/drivers/hid/introduction-to-hid-concepts
|
||||
|
||||
#include "EGAVHIDImplementation.h"
|
||||
|
||||
#include <sstream> // for ostringstream
|
||||
|
||||
// Windows headers; for HID interface
|
||||
#include <hidsdi.h>
|
||||
#pragma comment(lib,"hid.lib")
|
||||
#include <SetupAPI.h>
|
||||
#include <atlstr.h>
|
||||
|
||||
#pragma comment(lib, "setupapi.lib")
|
||||
|
||||
|
||||
#ifndef SAFE_CLOSE_HANDLE
|
||||
#define SAFE_CLOSE_HANDLE(_handle_) \
|
||||
{ \
|
||||
if ((_handle_ != 0) && (_handle_ != INVALID_HANDLE_VALUE))\
|
||||
{ \
|
||||
CloseHandle(_handle_); \
|
||||
_handle_= INVALID_HANDLE_VALUE; \
|
||||
} \
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string GetHIDDevicePath(int inIndex)
|
||||
{
|
||||
std::string devicePath;
|
||||
|
||||
GUID guid;
|
||||
HidD_GetHidGuid(&guid);
|
||||
|
||||
HDEVINFO DeviceInfo = SetupDiGetClassDevs(&guid, NULL, NULL, (DIGCF_PRESENT | DIGCF_DEVICEINTERFACE));
|
||||
|
||||
SP_DEVICE_INTERFACE_DATA DeviceInterface;
|
||||
DeviceInterface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
|
||||
|
||||
if (!SetupDiEnumDeviceInterfaces(DeviceInfo, NULL, &guid, inIndex, &DeviceInterface))
|
||||
{
|
||||
SetupDiDestroyDeviceInfoList(DeviceInfo);
|
||||
return "";
|
||||
}
|
||||
|
||||
unsigned long size = 0;
|
||||
SetupDiGetDeviceInterfaceDetail(DeviceInfo, &DeviceInterface, NULL, 0, &size, 0);
|
||||
|
||||
PSP_INTERFACE_DEVICE_DETAIL_DATA pDeviceDetail = (PSP_INTERFACE_DEVICE_DETAIL_DATA)malloc(size);
|
||||
if (pDeviceDetail)
|
||||
{
|
||||
pDeviceDetail->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);
|
||||
|
||||
if (SetupDiGetDeviceInterfaceDetail(DeviceInfo, &DeviceInterface, pDeviceDetail, size, &size, NULL))
|
||||
devicePath = CT2A(pDeviceDetail->DevicePath);
|
||||
free(pDeviceDetail);
|
||||
}
|
||||
|
||||
SetupDiDestroyDeviceInfoList(DeviceInfo);
|
||||
return devicePath;
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::shared_ptr<EGAVHIDInterface> CreateEGAVHIDInterface()
|
||||
{
|
||||
return std::make_shared<EGAVHID>();
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
// ## Class EGAVHID
|
||||
//==============================================================================
|
||||
|
||||
EGAVHID::EGAVHID()
|
||||
: mHIDCaps(std::make_unique<_HIDP_CAPS>())
|
||||
{
|
||||
}
|
||||
|
||||
EGAVResult EGAVHID::InitHIDInterface(const EGAVDeviceID& inDeviceID)
|
||||
{
|
||||
EGAVResult res = EGAVResult::ErrNotFound;
|
||||
|
||||
|
||||
DWORD index = 0;
|
||||
std::string path;
|
||||
while ((path = GetHIDDevicePath(index++)) != "")
|
||||
{
|
||||
HANDLE hHidDevice = CreateFileA(path.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
|
||||
if (hHidDevice == INVALID_HANDLE_VALUE)
|
||||
continue;
|
||||
|
||||
bool isCorrectHidDevice = false;
|
||||
HIDD_ATTRIBUTES attr;
|
||||
if (HidD_GetAttributes(hHidDevice, &attr))
|
||||
isCorrectHidDevice = (attr.VendorID == inDeviceID.vendorID && attr.ProductID == inDeviceID.productID);
|
||||
|
||||
if (isCorrectHidDevice)
|
||||
{
|
||||
mHIDHandle = hHidDevice;
|
||||
|
||||
{
|
||||
PHIDP_PREPARSED_DATA p;
|
||||
HidD_GetPreparsedData(mHIDHandle, &p);
|
||||
HIDP_CAPS c;
|
||||
HidP_GetCaps(p, &c);
|
||||
|
||||
*mHIDCaps = c;
|
||||
|
||||
HidD_FreePreparsedData(p);
|
||||
}
|
||||
|
||||
res = EGAVResult::Ok;
|
||||
break;
|
||||
}
|
||||
CloseHandle(hHidDevice);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
EGAVResult EGAVHID::DeinitHIDInterface()
|
||||
{
|
||||
SAFE_CLOSE_HANDLE(mHIDHandle);
|
||||
return EGAVResult::Ok;
|
||||
}
|
||||
|
||||
//! This reads the report from the hardware
|
||||
//! See Facemcam (Penna) sample code in https://elgato.atlassian.net/browse/EVH-493
|
||||
EGAVResult EGAVHID::ReadHID(std::vector<uint8_t>& outMessage, int inReportID, int inReadBufferSize/* = 0*/)
|
||||
{
|
||||
if (outMessage.size() >= mHIDCaps->InputReportByteLength)
|
||||
return EGAVResult::ErrInvalidParameter;
|
||||
|
||||
std::vector<uint8_t> inputReport(mHIDCaps->InputReportByteLength);
|
||||
|
||||
inputReport[0] = (uint8_t)inReportID;
|
||||
|
||||
if (inReadBufferSize > 0)
|
||||
inputReport.resize(inReadBufferSize); // Required for Cam Link PD575 (EVH-1418)
|
||||
|
||||
BOOL success = HidD_GetInputReport(mHIDHandle, &inputReport[0], (ULONG)inputReport.size());
|
||||
EGAVResult res = success ? EGAVResult::Ok : EGAVResult::ErrInvalidOperation;
|
||||
|
||||
// 121- ERROR_SEM_TIMEOUT
|
||||
// 31 - ERROR_GEN_FAILURE - for invalid report ID
|
||||
if (FALSE == success) // 87 - ERROR_INVALID_PARAMETER - if (buffer size != caps.InputReportByteLength)
|
||||
{
|
||||
// error_printf("HidD_GetInputReport() for report ID %d FAILED with %d", inReportID, GetLastError());
|
||||
}
|
||||
else
|
||||
{
|
||||
outMessage.assign(inputReport.begin(), inputReport.end());
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// this only prepends a zero byte (the report ID) to the message, pads it out to the size of an output
|
||||
// report and sends it to the hardware
|
||||
|
||||
EGAVResult EGAVHID::WriteHID(const std::vector<uint8_t>& inMessage, int inReportID)
|
||||
{
|
||||
if (!mHIDCaps)
|
||||
return EGAVResult::ErrInvalidState;
|
||||
|
||||
if (inMessage.size() > mHIDCaps->OutputReportByteLength-1)
|
||||
return EGAVResult::ErrInvalidParameter;
|
||||
|
||||
std::vector<uint8_t> outputReport(mHIDCaps->OutputReportByteLength, 0);
|
||||
|
||||
outputReport[0] = (uint8_t)inReportID;
|
||||
memcpy(&outputReport[1], &inMessage[0], inMessage.size());
|
||||
|
||||
// If the top-level collection includes report IDs, the caller must set the first byte of the ReportBuffer parameter to a non-zero report ID.
|
||||
BOOL success = HidD_SetOutputReport(mHIDHandle, &outputReport[0], (ULONG)outputReport.size());
|
||||
EGAVResult res = success ? EGAVResult::Ok : EGAVResult::ErrInvalidOperation;
|
||||
// 1167 - ERROR_DEVICE_NOT_CONNECTED
|
||||
if (FALSE == success) // 87 - ERROR_INVALID_PARAMETER - if (buffer size != c.OutputReportByteLength)
|
||||
{
|
||||
// error_printf("#### HID: HidD_SetOutputReport() FAILED with %d", GetLastError());
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
@ -0,0 +1,65 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Corsair Memory, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
@file EGAVHIDImplementation.h
|
||||
|
||||
@brief Windows implementation of EGAVHIDInterface
|
||||
**/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "EGAVHID.h"
|
||||
|
||||
struct _HIDP_CAPS;
|
||||
|
||||
class EGAVHID : public EGAVHIDInterface
|
||||
{
|
||||
public:
|
||||
EGAVHID();
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ## EGAVHIDInterface implementation
|
||||
//-----------------------------------------------------------------------------
|
||||
//! @param inIgnoreDevicePathCheck true for Cyclops because HID has a different DeviceID
|
||||
virtual EGAVResult InitHIDInterface(const EGAVDeviceID& inDeviceID) override;
|
||||
virtual EGAVResult DeinitHIDInterface() override;
|
||||
|
||||
//! @brief Reads a HID response message from the OS.
|
||||
//! @param outMessage will contain the resulting message. Its length will be adjusted automatically.
|
||||
virtual EGAVResult ReadHID(std::vector<uint8_t>& outMessage, int inReportID, int inReadBufferSize = 0) override;
|
||||
|
||||
//! @brief Writes a HID report (with ID 0) containing inMessage to the OS.
|
||||
//! @param inMessage the report contents, not including the report ID.
|
||||
virtual EGAVResult WriteHID(const std::vector<uint8_t>& inMessage, int inReportID) override;
|
||||
|
||||
HANDLE GetHIDHandle() { return mHIDHandle; }
|
||||
private:
|
||||
HANDLE mHIDHandle = nullptr;
|
||||
std::unique_ptr<_HIDP_CAPS> mHIDCaps;
|
||||
};
|
||||
156
obs-studio/deps/libdshowcapture/src/external/capture-device-support/SampleCode/DriverInterface.cpp
vendored
Normal file
156
obs-studio/deps/libdshowcapture/src/external/capture-device-support/SampleCode/DriverInterface.cpp
vendored
Normal file
@ -0,0 +1,156 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Corsair Memory, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
@file DriverInterface.cpp
|
||||
|
||||
@brief EGAVDeviceProperties class implementation
|
||||
Support for 4K60 Pro MK.2 and 4K60 S+
|
||||
**/
|
||||
//==============================================================================
|
||||
|
||||
#include "HDMIInfoFramesAPI.h"
|
||||
#include "DriverInterface.h"
|
||||
|
||||
#ifndef EGAV_OVERRIDE_DEBUG_MACROS
|
||||
inline void dummy() {}
|
||||
|
||||
#define warning_printf(...) dummy()
|
||||
|
||||
#ifndef HR_CHKRET_POINTER
|
||||
#define HR_CHKRET_POINTER(_ptr_) if (!(_ptr_)) return E_POINTER;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
//! Property IDs for IKsPropertySet
|
||||
enum class DriverProperty
|
||||
{
|
||||
XET_ENCODER_VIDEO_FORMAT = 400, //!< 4K60 S+ encoder format. uint32_t parameter: 0 - H.264 and 1 - HEVC
|
||||
GET_HDMI_HDR_PACKET_00_15 = 720, //!< HDMI HDR status packet - part 1
|
||||
GET_HDMI_HDR_PACKET_16_31 = 721, //!< HDMI HDR status packet - part 2
|
||||
XET_HDMI_HDR_TO_SDR = 722 //!< 4K60 Pro MK.2 Set HDR tonemapping. uint32_t parameter: 1 - on / 0 - off
|
||||
};
|
||||
|
||||
static const int HDMI_PACKET_SIZE = 32;
|
||||
|
||||
|
||||
EGAVDeviceProperties::EGAVDeviceProperties(IKsPropertySet* inKsPropertySet, DeviceType inDeviceType)
|
||||
: mICustomPropertySet(inKsPropertySet), mDeviceType(inDeviceType)
|
||||
{
|
||||
switch (inDeviceType)
|
||||
{
|
||||
case EGAVDeviceProperties::DeviceType::GC4K60ProMK2:
|
||||
mCustomPropertySetGUID = { 0xD1E5209F, 0x68FD, 0x4529, 0xBE, 0xE0, 0x5E, 0x7A, 0x1F, 0x47, 0x92, 0x26 };
|
||||
break;
|
||||
case EGAVDeviceProperties::DeviceType::GC4K60SPlus:
|
||||
mCustomPropertySetGUID = { 0xD1E5209F, 0x68FD, 0x4529, 0xBE, 0xE0, 0x5E, 0x7A, 0x1F, 0x47, 0x92, 0x24 };
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT EGAVDeviceProperties::SetEncoderType(bool inHEVC)
|
||||
{
|
||||
if (mDeviceType != DeviceType::GC4K60SPlus)
|
||||
return E_FAIL;
|
||||
|
||||
HR_CHKRET_POINTER(mICustomPropertySet);
|
||||
|
||||
uint32_t param = inHEVC ? 1 : 0;
|
||||
HRESULT hr = mICustomPropertySet->Set(mCustomPropertySetGUID, (DWORD)DriverProperty::XET_ENCODER_VIDEO_FORMAT, nullptr, 0, ¶m, sizeof(param));
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT EGAVDeviceProperties::SetHDRTonemapping(bool inEnable)
|
||||
{
|
||||
if (mDeviceType != DeviceType::GC4K60ProMK2)
|
||||
return E_FAIL;
|
||||
|
||||
HR_CHKRET_POINTER(mICustomPropertySet);
|
||||
|
||||
uint32_t param = inEnable ? 1 : 0;
|
||||
HRESULT hr = mICustomPropertySet->Set(mCustomPropertySetGUID, (DWORD)DriverProperty::XET_HDMI_HDR_TO_SDR, nullptr, 0, ¶m, sizeof(param));
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT EGAVDeviceProperties::GetHDMIHDRStatusPacket(uint8_t *outBuffer, int inBufferSize)
|
||||
{
|
||||
HR_CHKRET_POINTER(outBuffer);
|
||||
|
||||
if (inBufferSize < HDMI_PACKET_SIZE)
|
||||
return E_INVALIDARG;
|
||||
|
||||
HR_CHKRET_POINTER(mICustomPropertySet);
|
||||
|
||||
DWORD dwRet = 0;
|
||||
HRESULT hr = mICustomPropertySet->Get(mCustomPropertySetGUID, (DWORD)DriverProperty::GET_HDMI_HDR_PACKET_00_15, nullptr, 0, &outBuffer[0], 16, &dwRet);
|
||||
if (SUCCEEDED(hr))
|
||||
hr = mICustomPropertySet->Get(mCustomPropertySetGUID, (DWORD)DriverProperty::GET_HDMI_HDR_PACKET_16_31, nullptr, 0, &outBuffer[16], 16, &dwRet);
|
||||
return hr ;
|
||||
}
|
||||
|
||||
HRESULT EGAVDeviceProperties::IsVideoHDR(bool& outIsHDR)
|
||||
{
|
||||
outIsHDR = false;
|
||||
|
||||
// Try to read HDR meta data
|
||||
static const uint8_t emptyBuffer[HDMI_PACKET_SIZE] = { 0 };
|
||||
uint8_t buffer[HDMI_PACKET_SIZE] = { 0 };
|
||||
HRESULT hr = GetHDMIHDRStatusPacket(buffer, sizeof(buffer));
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
HDMI_GENERIC_INFOFRAME* frame = (HDMI_GENERIC_INFOFRAME*)(&buffer[0]);
|
||||
hr = (true == HDMI_IsInfoFrameValid(frame)) ? S_OK : E_FAIL;
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
// Check type in header and EOTF flag in payload
|
||||
if (HDMI_INFOFRAME_TYPE_DR == frame->header.bfType && HDMI_DR_EOTF_SDRGAMMA != frame->plDR1.bfEOTF)
|
||||
{
|
||||
outIsHDR = true;
|
||||
}
|
||||
else if (HDMI_INFOFRAME_TYPE_DR == frame->header.bfType && HDMI_DR_EOTF_SDRGAMMA == frame->plDR1.bfEOTF)
|
||||
{
|
||||
outIsHDR = false;
|
||||
}
|
||||
else if (0 /*HDMI_INFOFRAME_TYPE_RESERVED */ == frame->header.bfType && (0 == memcmp(buffer, emptyBuffer, sizeof(buffer))))
|
||||
{
|
||||
outIsHDR = false;
|
||||
}
|
||||
else if (HDMI_INFOFRAME_TYPE_DR != frame->header.bfType)
|
||||
{
|
||||
warning_printf("HDMI Metadata: Wrong header type: %d", frame->header.bfType);
|
||||
hr = E_FAIL;
|
||||
}
|
||||
}
|
||||
else
|
||||
warning_printf("HDMI Metadata: HDMI_IsInfoFrameValid() returned error (checksum)!");
|
||||
}
|
||||
else
|
||||
warning_printf("HDMI Metadata: GetHDMIHDRStatusPacket() failed!");
|
||||
|
||||
return hr;
|
||||
}
|
||||
72
obs-studio/deps/libdshowcapture/src/external/capture-device-support/SampleCode/DriverInterface.h
vendored
Normal file
72
obs-studio/deps/libdshowcapture/src/external/capture-device-support/SampleCode/DriverInterface.h
vendored
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022-23 Corsair Memory, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
@file DriverInterface.h
|
||||
|
||||
@brief EGAVDeviceProperties class implementation.
|
||||
Support for 4K60 Pro MK.2 and 4K60 S+
|
||||
**/
|
||||
//==============================================================================
|
||||
|
||||
|
||||
#include <atlcomcli.h>
|
||||
#include <strmif.h>
|
||||
|
||||
#include "HDMIInfoFramesAPI.h"
|
||||
|
||||
//! @brief Device properties for Elgato's non-UVC devices
|
||||
class EGAVDeviceProperties
|
||||
{
|
||||
public:
|
||||
enum class DeviceType
|
||||
{
|
||||
None = 0,
|
||||
GC4K60ProMK2, //!< 4K60 Pro MK.2: PCI\VEN_12AB& DEV_0710& SUBSYS_000E1CFA
|
||||
GC4K60SPlus //!< 4K60 S+: USB\VID_0FD9&PID_0068 or USB\VID_0FD9&PID_0075
|
||||
};
|
||||
|
||||
//! @brief
|
||||
//! @param inKsPropertySet Interface for driver property set.
|
||||
//! Can be queried from the DirectShow filter via IBaseFilter::QueryInterface()
|
||||
//! @param inDeviceType
|
||||
EGAVDeviceProperties(IKsPropertySet* inKsPropertySet, DeviceType inDeviceType);
|
||||
|
||||
//! @brief 4K60 S+ only
|
||||
//! @param inHEVC 1 - HEVC, 0 - H.264
|
||||
HRESULT SetEncoderType(bool inHEVC);
|
||||
|
||||
//! @brief 4K60 Pro MK.2 only
|
||||
//! @param inEnable 1 - enable tone mapping, 0 - disable HDR tonemapping
|
||||
HRESULT SetHDRTonemapping(bool inEnable);
|
||||
|
||||
HRESULT IsVideoHDR(bool& outIsHDR);
|
||||
HRESULT GetHDMIHDRStatusPacket(uint8_t* outBuffer, int inBufferSize);
|
||||
|
||||
private:
|
||||
DeviceType mDeviceType = DeviceType::None;
|
||||
GUID mCustomPropertySetGUID = GUID_NULL;
|
||||
CComPtr<IKsPropertySet> mICustomPropertySet;
|
||||
};
|
||||
91
obs-studio/deps/libdshowcapture/src/external/capture-device-support/SampleCode/main.cpp
vendored
Normal file
91
obs-studio/deps/libdshowcapture/src/external/capture-device-support/SampleCode/main.cpp
vendored
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022-23 Corsair Memory, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
#include "ElgatoUVCDevice.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
// # Constants
|
||||
//==============================================================================
|
||||
|
||||
const EGAVDeviceID& selectedDeviceID = deviceIDHD60SPlus; // Select your device here
|
||||
|
||||
|
||||
//==============================================================================
|
||||
// # main()
|
||||
//==============================================================================
|
||||
int main()
|
||||
{
|
||||
std::cout << "========================================" << std::endl;
|
||||
std::cout << " Sample: HDR Tonemapping" << std::endl;
|
||||
std::cout << "========================================" << std::endl;
|
||||
std::cout << std::endl;
|
||||
|
||||
std::shared_ptr<EGAVHIDInterface> hid = std::make_shared<EGAVHID>();
|
||||
EGAVResult res = hid->InitHIDInterface(selectedDeviceID);
|
||||
if (res.Failed())
|
||||
{
|
||||
std::cout << "InitHIDInterface() failed. Do you have the correct device connected?" << std::endl << std::endl;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
|
||||
}
|
||||
else
|
||||
{
|
||||
ElgatoUVCDevice device(hid, IsNewDeviceType(selectedDeviceID));
|
||||
HDMI_GENERIC_INFOFRAME frame{};
|
||||
memset(&frame, 0, sizeof(frame));
|
||||
res = device.GetHDMIHDRStatusPacket(frame);
|
||||
if (res.Succeeded())
|
||||
{
|
||||
bool isHDR = false;
|
||||
res = device.IsVideoHDR(isHDR);
|
||||
std::cout << "Video is " << (isHDR ? "HDR" : "SDR") << std::endl;
|
||||
if (res.Succeeded() && isHDR)
|
||||
{
|
||||
std::cout << "Disable HDR tonemapping" << std::endl;
|
||||
device.SetHDRTonemappingEnabled(false);
|
||||
|
||||
#if 1 // TEST: TOGGLE TONEMAPPINING
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
|
||||
std::cout << "Enable HDR tonemapping" << std::endl;
|
||||
device.SetHDRTonemappingEnabled(true);
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
|
||||
std::cout << "Disable HDR tonemapping" << std::endl;
|
||||
device.SetHDRTonemappingEnabled(false);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
hid->DeinitHIDInterface();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
63
obs-studio/deps/libdshowcapture/src/external/capture-device-support/readme.md
vendored
Normal file
63
obs-studio/deps/libdshowcapture/src/external/capture-device-support/readme.md
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
HID API for Elgato UVC devices
|
||||
==============================
|
||||
|
||||
The folder `Library` contains cross-platform code to access device-specific features of selected Elgato
|
||||
video products.
|
||||
|
||||
The CMake project in this folder builds a small console app for testing.
|
||||
Modify `selectedDeviceID` in `SampleCode/main.cpp` to select the correct device type.
|
||||
|
||||
Supported platforms
|
||||
-------------------
|
||||
* Windows (10 or higher)
|
||||
* macOS
|
||||
|
||||
Supported devices
|
||||
-----------------
|
||||
* HD60 S+
|
||||
* HD60 X
|
||||
|
||||
Supported features
|
||||
-----------------
|
||||
* Switch on-device HDR tonemapping on/off
|
||||
* Read HDMI HDR status packet (for HDR detection)
|
||||
|
||||
Limitations
|
||||
-----------
|
||||
The library was written for macOS and Windows.
|
||||
However, the sample project was only built with Visual Studio 2019 and tested on Windows so far.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Driver API for Elgato devices
|
||||
=============================
|
||||
|
||||
For non-UVC devices device properties can be accessed via a custom driver property set (`IKsPropertySet`).
|
||||
|
||||
Sample code is provided, see `SampleCode/DriverInterface.h/.cpp`
|
||||
|
||||
Supported platforms
|
||||
-------------------
|
||||
* Windows (10 or higher)
|
||||
|
||||
Supported devices
|
||||
-----------------
|
||||
* 4K60 Pro MK.2
|
||||
* 4K60 S+
|
||||
|
||||
Supported features
|
||||
------------------
|
||||
* Switch on-device HDR tonemapping on/off (4K60 Pro MK.2 only)
|
||||
* Set video compression (4K60 S+ only)
|
||||
* Read HDMI HDR status packet (for HDR detection)
|
||||
|
||||
#### 4K60 Pro MK.2
|
||||
|
||||
To receive HDR the format on DirectShow filter pin must be set to P010.
|
||||
|
||||
#### 4K60 S+
|
||||
|
||||
The 4K60 S+ always produces compressed video output.
|
||||
For HDR the encoder format must be set to HEVC via the driver interface.
|
||||
|
||||
|
||||
49
obs-studio/deps/libdshowcapture/src/source/CoTaskMemPtr.hpp
Normal file
49
obs-studio/deps/libdshowcapture/src/source/CoTaskMemPtr.hpp
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Lain Bailey <lain@obsproject.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
template<typename T> class CoTaskMemPtr {
|
||||
T *ptr;
|
||||
|
||||
inline void Clear()
|
||||
{
|
||||
if (ptr)
|
||||
CoTaskMemFree(ptr);
|
||||
}
|
||||
|
||||
public:
|
||||
inline CoTaskMemPtr() : ptr(NULL) {}
|
||||
inline CoTaskMemPtr(T *ptr_) : ptr(ptr_) {}
|
||||
inline ~CoTaskMemPtr() { Clear(); }
|
||||
|
||||
inline operator T *() const { return ptr; }
|
||||
inline T *operator->() const { return ptr; }
|
||||
|
||||
inline CoTaskMemPtr &operator=(T *val)
|
||||
{
|
||||
Clear();
|
||||
ptr = val;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline T **operator&()
|
||||
{
|
||||
Clear();
|
||||
ptr = NULL;
|
||||
return &ptr;
|
||||
}
|
||||
};
|
||||
156
obs-studio/deps/libdshowcapture/src/source/ComPtr.hpp
Normal file
156
obs-studio/deps/libdshowcapture/src/source/ComPtr.hpp
Normal file
@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Lain Bailey <lain@obsproject.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/* Oh no I have my own com pointer class, the world is ending, how dare you
|
||||
* write your own! */
|
||||
|
||||
template<class T> class ComPtr {
|
||||
|
||||
protected:
|
||||
T *ptr;
|
||||
|
||||
inline void Kill()
|
||||
{
|
||||
if (ptr)
|
||||
ptr->Release();
|
||||
}
|
||||
|
||||
inline void Replace(T *p)
|
||||
{
|
||||
if (ptr != p) {
|
||||
if (p)
|
||||
p->AddRef();
|
||||
if (ptr)
|
||||
ptr->Release();
|
||||
ptr = p;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
inline ComPtr() : ptr(nullptr) {}
|
||||
inline ComPtr(T *p) : ptr(p)
|
||||
{
|
||||
if (ptr)
|
||||
ptr->AddRef();
|
||||
}
|
||||
inline ComPtr(const ComPtr<T> &c) : ptr(c.ptr)
|
||||
{
|
||||
if (ptr)
|
||||
ptr->AddRef();
|
||||
}
|
||||
inline ComPtr(ComPtr<T> &&c) noexcept : ptr(c.ptr) { c.ptr = nullptr; }
|
||||
inline ~ComPtr() { Kill(); }
|
||||
|
||||
inline void Clear()
|
||||
{
|
||||
if (ptr) {
|
||||
ptr->Release();
|
||||
ptr = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
inline ComPtr<T> &operator=(T *p)
|
||||
{
|
||||
Replace(p);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline ComPtr<T> &operator=(const ComPtr<T> &c)
|
||||
{
|
||||
Replace(c.ptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline ComPtr<T> &operator=(ComPtr<T> &&c) noexcept
|
||||
{
|
||||
if (&ptr != &c.ptr) {
|
||||
Kill();
|
||||
ptr = c.ptr;
|
||||
c.ptr = nullptr;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline T *Detach()
|
||||
{
|
||||
T *out = ptr;
|
||||
ptr = nullptr;
|
||||
return out;
|
||||
}
|
||||
|
||||
inline void CopyTo(T **out)
|
||||
{
|
||||
if (out) {
|
||||
if (ptr)
|
||||
ptr->AddRef();
|
||||
*out = ptr;
|
||||
}
|
||||
}
|
||||
|
||||
inline ULONG Release()
|
||||
{
|
||||
ULONG ref;
|
||||
|
||||
if (!ptr)
|
||||
return 0;
|
||||
ref = ptr->Release();
|
||||
ptr = nullptr;
|
||||
return ref;
|
||||
}
|
||||
|
||||
inline T **Assign()
|
||||
{
|
||||
Clear();
|
||||
return &ptr;
|
||||
}
|
||||
inline void Set(T *p)
|
||||
{
|
||||
Kill();
|
||||
ptr = p;
|
||||
}
|
||||
|
||||
inline T *Get() const { return ptr; }
|
||||
|
||||
inline T **operator&() { return Assign(); }
|
||||
|
||||
inline operator T *() const { return ptr; }
|
||||
inline T *operator->() const { return ptr; }
|
||||
|
||||
inline bool operator==(T *p) const { return ptr == p; }
|
||||
inline bool operator!=(T *p) const { return ptr != p; }
|
||||
|
||||
inline bool operator!() const { return !ptr; }
|
||||
};
|
||||
|
||||
template<class T> class ComQIPtr : public ComPtr<T> {
|
||||
|
||||
public:
|
||||
inline ComQIPtr(IUnknown *unk)
|
||||
{
|
||||
this->ptr = nullptr;
|
||||
unk->QueryInterface(__uuidof(T), (void **)&this->ptr);
|
||||
}
|
||||
|
||||
inline ComPtr<T> &operator=(IUnknown *unk)
|
||||
{
|
||||
ComPtr<T>::Clear();
|
||||
unk->QueryInterface(__uuidof(T), (void **)&this->ptr);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* aver_prophwencode.h -- This header filer is provide to 3rd party AP
|
||||
* to use the HW encode function of AVerMedia.
|
||||
*
|
||||
* Copyright (C) 2014 AVerMedia TECHNOLOGIES, Inc.
|
||||
*
|
||||
* Authors: Morris Pan, AVerMedia TECHNOLOGIES, Inc. <morris.pan@avermedia.com>
|
||||
*
|
||||
* This content is released under the MIT License
|
||||
* (http://opensource.org/licenses/MIT).
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
static const GUID AVER_HW_ENCODE_PROPERTY = {0x1bd55918,
|
||||
0xbaf5,
|
||||
0x4781,
|
||||
{0x8d, 0x76, 0xe0, 0xa0, 0xa5,
|
||||
0xe1, 0xd2, 0xb8}};
|
||||
|
||||
enum {
|
||||
// @brief PropertySet Enumeration
|
||||
// param AVER_PARAMETERS
|
||||
PROPERTY_HW_ENCODE_PARAMETER = 0
|
||||
};
|
||||
|
||||
enum {
|
||||
// property to set/get the encode frame rate
|
||||
// ulParam1 = Frames per second
|
||||
AVER_PARAMETER_ENCODE_FRAME_RATE = 0,
|
||||
|
||||
// property to set/get the encode bit rate
|
||||
// ulParam1 = Bitrate (kb/s)
|
||||
AVER_PARAMETER_ENCODE_BIT_RATE = 1,
|
||||
|
||||
// property to get the output resolution
|
||||
// ulParam1 = Resolution width
|
||||
// ulParam2 = Resolution height
|
||||
AVER_PARAMETER_CURRENT_RESOLUTION = 2,
|
||||
|
||||
// property to set the output resolution
|
||||
// ulParam1 = Resolution width
|
||||
// ulParam2 = Resolution height
|
||||
AVER_PARAMETER_ENCODE_RESOLUTION = 3,
|
||||
|
||||
// property to set/get the encode GOP
|
||||
// ulParam1 = GOP length
|
||||
AVER_PARAMETER_ENCODE_GOP = 4,
|
||||
|
||||
// property to insert an I frame to the encoded stream
|
||||
AVER_PARAMETER_INSERT_I_FRAME = 6
|
||||
};
|
||||
|
||||
struct AVER_PARAMETERS {
|
||||
// @brief Use the PROPERTY_PARAMETER Property to Get or Set the
|
||||
// Device Parameter.
|
||||
//
|
||||
// param ulIndex Parameter Index (AVER_PARAMETER_*)
|
||||
// param ulParam1 Parameter 1 (if any)
|
||||
// param ulParam2 Parameter 2 (if any)
|
||||
// param ulParam3 Parameter 3 (if any)
|
||||
ULONG ulIndex;
|
||||
ULONG ulParam1;
|
||||
ULONG ulParam2;
|
||||
ULONG ulParam3;
|
||||
};
|
||||
696
obs-studio/deps/libdshowcapture/src/source/capture-filter.cpp
Normal file
696
obs-studio/deps/libdshowcapture/src/source/capture-filter.cpp
Normal file
@ -0,0 +1,696 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Lain Bailey <lain@obsproject.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
#include "capture-filter.hpp"
|
||||
#include "log.hpp"
|
||||
|
||||
namespace DShow {
|
||||
|
||||
#if 0
|
||||
#define PrintFunc(x) Debug(x)
|
||||
#else
|
||||
#define PrintFunc(x)
|
||||
#endif
|
||||
|
||||
#define FILTER_NAME L"Capture Filter"
|
||||
#define VIDEO_PIN_NAME L"Video Capture"
|
||||
#define AUDIO_PIN_NAME L"Audio Capture"
|
||||
|
||||
CapturePin::CapturePin(CaptureFilter *filter_, const PinCaptureInfo &info)
|
||||
: refCount(0), captureInfo(info), filter(filter_)
|
||||
{
|
||||
connectedMediaType->majortype = info.expectedMajorType;
|
||||
}
|
||||
|
||||
CapturePin::~CapturePin() {}
|
||||
|
||||
STDMETHODIMP CapturePin::QueryInterface(REFIID riid, void **ppv)
|
||||
{
|
||||
if (riid == IID_IUnknown) {
|
||||
AddRef();
|
||||
*ppv = this;
|
||||
} else if (riid == IID_IPin) {
|
||||
AddRef();
|
||||
*ppv = (IPin *)this;
|
||||
} else if (riid == IID_IMemInputPin) {
|
||||
AddRef();
|
||||
*ppv = (IMemInputPin *)this;
|
||||
} else {
|
||||
*ppv = nullptr;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
STDMETHODIMP_(ULONG) CapturePin::AddRef()
|
||||
{
|
||||
return (ULONG)InterlockedIncrement(&refCount);
|
||||
}
|
||||
|
||||
STDMETHODIMP_(ULONG) CapturePin::Release()
|
||||
{
|
||||
if (!InterlockedDecrement(&refCount)) {
|
||||
delete this;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (ULONG)refCount;
|
||||
}
|
||||
|
||||
// IPin methods
|
||||
STDMETHODIMP CapturePin::Connect(IPin *pReceivePin, const AM_MEDIA_TYPE *pmt)
|
||||
{
|
||||
PrintFunc(L"CapturePin::Connect");
|
||||
|
||||
if (filter->state == State_Running)
|
||||
return VFW_E_NOT_STOPPED;
|
||||
|
||||
if (connectedPin)
|
||||
return VFW_E_ALREADY_CONNECTED;
|
||||
|
||||
if (!pmt)
|
||||
return S_OK;
|
||||
|
||||
if (pmt->majortype != GUID_NULL &&
|
||||
pmt->majortype != captureInfo.expectedMajorType)
|
||||
return S_FALSE;
|
||||
|
||||
if (pmt->majortype == captureInfo.expectedMajorType &&
|
||||
!IsValidMediaType(pmt))
|
||||
return S_FALSE;
|
||||
|
||||
DSHOW_UNUSED(pReceivePin);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CapturePin::ReceiveConnection(IPin *pConnector,
|
||||
const AM_MEDIA_TYPE *pmt)
|
||||
{
|
||||
PrintFunc(L"CapturePin::ReceiveConnection");
|
||||
|
||||
if (filter->state != State_Stopped)
|
||||
return VFW_E_NOT_STOPPED;
|
||||
if (!pConnector || !pmt)
|
||||
return E_POINTER;
|
||||
if (connectedPin)
|
||||
return VFW_E_ALREADY_CONNECTED;
|
||||
|
||||
if (QueryAccept(pmt) != S_OK)
|
||||
return VFW_E_TYPE_NOT_ACCEPTED;
|
||||
|
||||
connectedPin = pConnector;
|
||||
connectedMediaType = pmt;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CapturePin::Disconnect()
|
||||
{
|
||||
PrintFunc(L"CapturePin::Disconnect");
|
||||
|
||||
if (!connectedPin)
|
||||
return S_FALSE;
|
||||
|
||||
connectedPin = nullptr;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CapturePin::ConnectedTo(IPin **pPin)
|
||||
{
|
||||
PrintFunc(L"CapturePin::ConnectedTo");
|
||||
|
||||
if (!connectedPin)
|
||||
return VFW_E_NOT_CONNECTED;
|
||||
|
||||
IPin *pin = connectedPin;
|
||||
pin->AddRef();
|
||||
*pPin = pin;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CapturePin::ConnectionMediaType(AM_MEDIA_TYPE *pmt)
|
||||
{
|
||||
PrintFunc(L"CapturePin::ConnectionMediaType");
|
||||
|
||||
if (!connectedPin)
|
||||
return VFW_E_NOT_CONNECTED;
|
||||
|
||||
return CopyMediaType(pmt, connectedMediaType);
|
||||
}
|
||||
|
||||
STDMETHODIMP CapturePin::QueryPinInfo(PIN_INFO *pInfo)
|
||||
{
|
||||
PrintFunc(L"CapturePin::QueryPinInfo");
|
||||
|
||||
pInfo->pFilter = filter;
|
||||
if (filter) {
|
||||
IBaseFilter *ptr = filter;
|
||||
ptr->AddRef();
|
||||
}
|
||||
|
||||
if (captureInfo.expectedMajorType == MEDIATYPE_Video)
|
||||
memcpy(pInfo->achName, VIDEO_PIN_NAME, sizeof(VIDEO_PIN_NAME));
|
||||
else
|
||||
memcpy(pInfo->achName, AUDIO_PIN_NAME, sizeof(AUDIO_PIN_NAME));
|
||||
|
||||
pInfo->dir = PINDIR_INPUT;
|
||||
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
STDMETHODIMP CapturePin::QueryDirection(PIN_DIRECTION *pPinDir)
|
||||
{
|
||||
*pPinDir = PINDIR_INPUT;
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
#define CAPTURE_PIN_NAME L"Capture Pin"
|
||||
|
||||
STDMETHODIMP CapturePin::QueryId(LPWSTR *lpId)
|
||||
{
|
||||
wchar_t *str = (wchar_t *)CoTaskMemAlloc(sizeof(CAPTURE_PIN_NAME));
|
||||
memcpy(str, CAPTURE_PIN_NAME, sizeof(CAPTURE_PIN_NAME));
|
||||
*lpId = str;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CapturePin::QueryAccept(const AM_MEDIA_TYPE *pmt)
|
||||
{
|
||||
PrintFunc(L"CapturePin::QueryAccept");
|
||||
|
||||
if (pmt->majortype != captureInfo.expectedMajorType)
|
||||
return S_FALSE;
|
||||
if (!IsValidMediaType(pmt))
|
||||
return S_FALSE;
|
||||
|
||||
if (connectedPin)
|
||||
connectedMediaType = pmt;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CapturePin::EnumMediaTypes(IEnumMediaTypes **ppEnum)
|
||||
{
|
||||
PrintFunc(L"CapturePin::EnumMediaTypes");
|
||||
|
||||
*ppEnum = new CaptureEnumMediaTypes(this);
|
||||
if (!*ppEnum)
|
||||
return E_OUTOFMEMORY;
|
||||
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
STDMETHODIMP CapturePin::QueryInternalConnections(IPin **apPin, ULONG *nPin)
|
||||
{
|
||||
PrintFunc(L"CapturePin::QueryInternalConnections");
|
||||
|
||||
DSHOW_UNUSED(apPin);
|
||||
DSHOW_UNUSED(nPin);
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
STDMETHODIMP CapturePin::EndOfStream()
|
||||
{
|
||||
PrintFunc(L"CapturePin::EndOfStream");
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CapturePin::BeginFlush()
|
||||
{
|
||||
PrintFunc(L"CapturePin::BeginFlush");
|
||||
|
||||
flushing = true;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CapturePin::EndFlush()
|
||||
{
|
||||
PrintFunc(L"CapturePin::EndFlush");
|
||||
|
||||
flushing = false;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CapturePin::NewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop,
|
||||
double dRate)
|
||||
{
|
||||
PrintFunc(L"CapturePin::NewSegment");
|
||||
|
||||
DSHOW_UNUSED(tStart);
|
||||
DSHOW_UNUSED(tStop);
|
||||
DSHOW_UNUSED(dRate);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// IMemInputPin methods
|
||||
STDMETHODIMP CapturePin::GetAllocator(IMemAllocator **ppAllocator)
|
||||
{
|
||||
PrintFunc(L"CapturePin::GetAllocator");
|
||||
|
||||
DSHOW_UNUSED(ppAllocator);
|
||||
return VFW_E_NO_ALLOCATOR;
|
||||
}
|
||||
|
||||
STDMETHODIMP CapturePin::NotifyAllocator(IMemAllocator *pAllocator,
|
||||
BOOL bReadOnly)
|
||||
{
|
||||
PrintFunc(L"CapturePin::NotifyAllocator");
|
||||
|
||||
DSHOW_UNUSED(pAllocator);
|
||||
DSHOW_UNUSED(bReadOnly);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CapturePin::GetAllocatorRequirements(ALLOCATOR_PROPERTIES *pProps)
|
||||
{
|
||||
PrintFunc(L"CapturePin::GetAllocatorRequirements");
|
||||
|
||||
DSHOW_UNUSED(pProps);
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
STDMETHODIMP CapturePin::Receive(IMediaSample *pSample)
|
||||
{
|
||||
PrintFunc(L"CapturePin::Receive");
|
||||
|
||||
if (flushing)
|
||||
return S_FALSE;
|
||||
|
||||
if (pSample)
|
||||
captureInfo.callback(pSample);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CapturePin::ReceiveMultiple(IMediaSample **pSamples, long nSamples,
|
||||
long *nSamplesProcessed)
|
||||
{
|
||||
PrintFunc(L"CapturePin::ReceiveMultiple");
|
||||
|
||||
if (flushing)
|
||||
return S_FALSE;
|
||||
|
||||
for (long i = 0; i < nSamples; i++)
|
||||
if (pSamples[i])
|
||||
captureInfo.callback(pSamples[i]);
|
||||
|
||||
*nSamplesProcessed = nSamples;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CapturePin::ReceiveCanBlock()
|
||||
{
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
bool CapturePin::IsValidMediaType(const AM_MEDIA_TYPE *pmt) const
|
||||
{
|
||||
if (pmt->pbFormat) {
|
||||
if (pmt->subtype != captureInfo.expectedSubType ||
|
||||
pmt->majortype != captureInfo.expectedMajorType)
|
||||
return false;
|
||||
|
||||
if (captureInfo.expectedMajorType == MEDIATYPE_Video) {
|
||||
const BITMAPINFOHEADER *bih = GetBitmapInfoHeader(*pmt);
|
||||
if (!bih || bih->biHeight == 0 || bih->biWidth == 0)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
class MiscFlagsHandler : public IAMFilterMiscFlags {
|
||||
volatile long refCount = 0;
|
||||
|
||||
public:
|
||||
inline MiscFlagsHandler() {}
|
||||
virtual ~MiscFlagsHandler() {}
|
||||
|
||||
STDMETHODIMP QueryInterface(REFIID riid, void **ppv)
|
||||
{
|
||||
if (riid == IID_IUnknown) {
|
||||
AddRef();
|
||||
*ppv = this;
|
||||
} else {
|
||||
*ppv = nullptr;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
STDMETHODIMP_(ULONG) AddRef()
|
||||
{
|
||||
return InterlockedIncrement(&refCount);
|
||||
}
|
||||
|
||||
STDMETHODIMP_(ULONG) Release()
|
||||
{
|
||||
if (!InterlockedDecrement(&refCount)) {
|
||||
delete this;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return refCount;
|
||||
}
|
||||
|
||||
STDMETHODIMP_(ULONG) GetMiscFlags()
|
||||
{
|
||||
return AM_FILTER_MISC_FLAGS_IS_RENDERER;
|
||||
}
|
||||
};
|
||||
|
||||
CaptureFilter::CaptureFilter(const PinCaptureInfo &info)
|
||||
: refCount(0),
|
||||
state(State_Stopped),
|
||||
pin(new CapturePin(this, info)),
|
||||
misc(new MiscFlagsHandler)
|
||||
{
|
||||
}
|
||||
|
||||
CaptureFilter::~CaptureFilter() {}
|
||||
|
||||
// IUnknown methods
|
||||
STDMETHODIMP CaptureFilter::QueryInterface(REFIID riid, void **ppv)
|
||||
{
|
||||
if (riid == IID_IUnknown) {
|
||||
AddRef();
|
||||
*ppv = this;
|
||||
} else if (riid == IID_IPersist) {
|
||||
AddRef();
|
||||
*ppv = (IPersist *)this;
|
||||
} else if (riid == IID_IMediaFilter) {
|
||||
AddRef();
|
||||
*ppv = (IMediaFilter *)this;
|
||||
} else if (riid == IID_IBaseFilter) {
|
||||
AddRef();
|
||||
*ppv = (IBaseFilter *)this;
|
||||
} else if (riid == IID_IAMFilterMiscFlags) {
|
||||
misc.CopyTo((IAMFilterMiscFlags **)ppv);
|
||||
} else {
|
||||
*ppv = nullptr;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
STDMETHODIMP_(ULONG) CaptureFilter::AddRef()
|
||||
{
|
||||
return InterlockedIncrement(&refCount);
|
||||
}
|
||||
|
||||
STDMETHODIMP_(ULONG) CaptureFilter::Release()
|
||||
{
|
||||
if (!InterlockedDecrement(&refCount)) {
|
||||
delete this;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return refCount;
|
||||
}
|
||||
|
||||
// IPersist method
|
||||
STDMETHODIMP CaptureFilter::GetClassID(CLSID *pClsID)
|
||||
{
|
||||
DSHOW_UNUSED(pClsID);
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
// IMediaFilter methods
|
||||
STDMETHODIMP CaptureFilter::GetState(DWORD dwMSecs, FILTER_STATE *State)
|
||||
{
|
||||
PrintFunc(L"CaptureFilter::GetState");
|
||||
|
||||
*State = state;
|
||||
|
||||
DSHOW_UNUSED(dwMSecs);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CaptureFilter::SetSyncSource(IReferenceClock *pClock)
|
||||
{
|
||||
DSHOW_UNUSED(pClock);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CaptureFilter::GetSyncSource(IReferenceClock **pClock)
|
||||
{
|
||||
*pClock = nullptr;
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
STDMETHODIMP CaptureFilter::Stop()
|
||||
{
|
||||
PrintFunc(L"CaptureFilter::Stop");
|
||||
|
||||
state = State_Stopped;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CaptureFilter::Pause()
|
||||
{
|
||||
PrintFunc(L"CaptureFilter::Pause");
|
||||
|
||||
state = State_Paused;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CaptureFilter::Run(REFERENCE_TIME tStart)
|
||||
{
|
||||
PrintFunc(L"CaptureFilter::Run");
|
||||
|
||||
state = State_Running;
|
||||
|
||||
DSHOW_UNUSED(tStart);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// IBaseFilter methods
|
||||
STDMETHODIMP CaptureFilter::EnumPins(IEnumPins **ppEnum)
|
||||
{
|
||||
PrintFunc(L"CaptureFilter::EnumPins");
|
||||
|
||||
*ppEnum = new CaptureEnumPins(this, nullptr);
|
||||
return (*ppEnum == nullptr) ? E_OUTOFMEMORY : NOERROR;
|
||||
}
|
||||
|
||||
STDMETHODIMP CaptureFilter::FindPin(LPCWSTR Id, IPin **ppPin)
|
||||
{
|
||||
PrintFunc(L"CaptureFilter::FindPin");
|
||||
|
||||
if (Id == nullptr || ppPin == nullptr)
|
||||
return E_POINTER;
|
||||
|
||||
if (lstrcmpW(Id, CAPTURE_PIN_NAME) == 0) {
|
||||
*ppPin = pin;
|
||||
pin->AddRef();
|
||||
return S_OK;
|
||||
} else {
|
||||
*ppPin = nullptr;
|
||||
return VFW_E_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
STDMETHODIMP CaptureFilter::QueryFilterInfo(FILTER_INFO *pInfo)
|
||||
{
|
||||
PrintFunc(L"CaptureFilter::QueryFilterInfo");
|
||||
|
||||
memcpy(pInfo->achName, FILTER_NAME, sizeof(FILTER_NAME));
|
||||
|
||||
pInfo->pGraph = graph;
|
||||
if (graph) {
|
||||
IFilterGraph *graph_ptr = graph;
|
||||
graph_ptr->AddRef();
|
||||
}
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
STDMETHODIMP CaptureFilter::JoinFilterGraph(IFilterGraph *pGraph, LPCWSTR pName)
|
||||
{
|
||||
DSHOW_UNUSED(pName);
|
||||
|
||||
graph = pGraph;
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
STDMETHODIMP CaptureFilter::QueryVendorInfo(LPWSTR *pVendorInfo)
|
||||
{
|
||||
DSHOW_UNUSED(pVendorInfo);
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
CaptureEnumPins::CaptureEnumPins(CaptureFilter *filter_, CaptureEnumPins *pEnum)
|
||||
: filter(filter_)
|
||||
{
|
||||
curPin = (pEnum != nullptr) ? pEnum->curPin : 0;
|
||||
}
|
||||
|
||||
CaptureEnumPins::~CaptureEnumPins() {}
|
||||
|
||||
// IUnknown
|
||||
STDMETHODIMP CaptureEnumPins::QueryInterface(REFIID riid, void **ppv)
|
||||
{
|
||||
if (riid == IID_IUnknown || riid == IID_IEnumPins) {
|
||||
AddRef();
|
||||
*ppv = (IEnumPins *)this;
|
||||
return NOERROR;
|
||||
} else {
|
||||
*ppv = nullptr;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
}
|
||||
|
||||
STDMETHODIMP_(ULONG) CaptureEnumPins::AddRef()
|
||||
{
|
||||
return (ULONG)InterlockedIncrement(&refCount);
|
||||
}
|
||||
|
||||
STDMETHODIMP_(ULONG) CaptureEnumPins::Release()
|
||||
{
|
||||
if (!InterlockedDecrement(&refCount)) {
|
||||
delete this;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (ULONG)refCount;
|
||||
}
|
||||
|
||||
// IEnumPins
|
||||
STDMETHODIMP CaptureEnumPins::Next(ULONG cPins, IPin **ppPins, ULONG *pcFetched)
|
||||
{
|
||||
UINT nFetched = 0;
|
||||
|
||||
if (curPin == 0 && cPins > 0) {
|
||||
IPin *pPin = filter->GetPin();
|
||||
|
||||
*ppPins = pPin;
|
||||
pPin->AddRef();
|
||||
|
||||
nFetched = 1;
|
||||
curPin++;
|
||||
}
|
||||
|
||||
if (pcFetched)
|
||||
*pcFetched = nFetched;
|
||||
|
||||
return (nFetched == cPins) ? S_OK : S_FALSE;
|
||||
}
|
||||
|
||||
STDMETHODIMP CaptureEnumPins::Skip(ULONG cPins)
|
||||
{
|
||||
return ((curPin += cPins) > 1) ? S_FALSE : S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CaptureEnumPins::Reset()
|
||||
{
|
||||
curPin = 0;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CaptureEnumPins::Clone(IEnumPins **ppEnum)
|
||||
{
|
||||
*ppEnum = new CaptureEnumPins(filter, this);
|
||||
return (*ppEnum == nullptr) ? E_OUTOFMEMORY : NOERROR;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
CaptureEnumMediaTypes::CaptureEnumMediaTypes(CapturePin *pin_) : pin(pin_) {}
|
||||
|
||||
CaptureEnumMediaTypes::~CaptureEnumMediaTypes() {}
|
||||
|
||||
STDMETHODIMP CaptureEnumMediaTypes::QueryInterface(REFIID riid, void **ppv)
|
||||
{
|
||||
if (riid == IID_IUnknown || riid == IID_IEnumMediaTypes) {
|
||||
AddRef();
|
||||
*ppv = this;
|
||||
return NOERROR;
|
||||
} else {
|
||||
*ppv = nullptr;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
}
|
||||
|
||||
STDMETHODIMP_(ULONG) CaptureEnumMediaTypes::AddRef()
|
||||
{
|
||||
return (ULONG)InterlockedIncrement(&refCount);
|
||||
}
|
||||
|
||||
STDMETHODIMP_(ULONG) CaptureEnumMediaTypes::Release()
|
||||
{
|
||||
if (!InterlockedDecrement(&refCount)) {
|
||||
delete this;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (ULONG)refCount;
|
||||
}
|
||||
|
||||
// IEnumMediaTypes
|
||||
STDMETHODIMP CaptureEnumMediaTypes::Next(ULONG cMediaTypes,
|
||||
AM_MEDIA_TYPE **ppMediaTypes,
|
||||
ULONG *pcFetched)
|
||||
{
|
||||
PrintFunc(L"CaptureEnumMediaTypes::Next");
|
||||
|
||||
UINT nFetched = 0;
|
||||
|
||||
if (curMT == 0 && cMediaTypes > 0) {
|
||||
*ppMediaTypes = pin->connectedMediaType.Duplicate();
|
||||
nFetched = 1;
|
||||
curMT++;
|
||||
}
|
||||
|
||||
if (pcFetched)
|
||||
*pcFetched = nFetched;
|
||||
|
||||
return (nFetched == cMediaTypes) ? S_OK : S_FALSE;
|
||||
}
|
||||
|
||||
STDMETHODIMP CaptureEnumMediaTypes::Skip(ULONG cMediaTypes)
|
||||
{
|
||||
PrintFunc(L"CaptureEnumMediaTypes::Skip");
|
||||
|
||||
return ((curMT += cMediaTypes) > 1) ? S_FALSE : S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CaptureEnumMediaTypes::Reset()
|
||||
{
|
||||
PrintFunc(L"CaptureEnumMediaTypes::Reset");
|
||||
|
||||
curMT = 0;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CaptureEnumMediaTypes::Clone(IEnumMediaTypes **ppEnum)
|
||||
{
|
||||
*ppEnum = new CaptureEnumMediaTypes(pin);
|
||||
return (*ppEnum == nullptr) ? E_OUTOFMEMORY : NOERROR;
|
||||
}
|
||||
|
||||
}; /* namespace DShow */
|
||||
173
obs-studio/deps/libdshowcapture/src/source/capture-filter.hpp
Normal file
173
obs-studio/deps/libdshowcapture/src/source/capture-filter.hpp
Normal file
@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Lain Bailey <lain@obsproject.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "dshow-base.hpp"
|
||||
#include "dshow-media-type.hpp"
|
||||
#include "../dshowcapture.hpp"
|
||||
|
||||
namespace DShow {
|
||||
|
||||
class CaptureFilter;
|
||||
class CaptureSource;
|
||||
|
||||
typedef void (*CaptureCallback)(void *param, IMediaSample *sample);
|
||||
|
||||
struct PinCaptureInfo {
|
||||
std::function<void(IMediaSample *sample)> callback;
|
||||
GUID expectedMajorType{};
|
||||
GUID expectedSubType{};
|
||||
};
|
||||
|
||||
class CapturePin : public IPin, public IMemInputPin {
|
||||
friend class CaptureEnumMediaTypes;
|
||||
|
||||
volatile long refCount;
|
||||
|
||||
PinCaptureInfo captureInfo;
|
||||
ComPtr<IPin> connectedPin;
|
||||
CaptureFilter *filter;
|
||||
MediaType connectedMediaType;
|
||||
volatile bool flushing = false;
|
||||
|
||||
bool IsValidMediaType(const AM_MEDIA_TYPE *pmt) const;
|
||||
|
||||
public:
|
||||
CapturePin(CaptureFilter *filter, const PinCaptureInfo &info);
|
||||
virtual ~CapturePin();
|
||||
|
||||
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
|
||||
STDMETHODIMP_(ULONG) AddRef();
|
||||
STDMETHODIMP_(ULONG) Release();
|
||||
|
||||
// IPin methods
|
||||
STDMETHODIMP Connect(IPin *pReceivePin, const AM_MEDIA_TYPE *pmt);
|
||||
STDMETHODIMP ReceiveConnection(IPin *connector,
|
||||
const AM_MEDIA_TYPE *pmt);
|
||||
STDMETHODIMP Disconnect();
|
||||
STDMETHODIMP ConnectedTo(IPin **pPin);
|
||||
STDMETHODIMP ConnectionMediaType(AM_MEDIA_TYPE *pmt);
|
||||
STDMETHODIMP QueryPinInfo(PIN_INFO *pInfo);
|
||||
STDMETHODIMP QueryDirection(PIN_DIRECTION *pPinDir);
|
||||
STDMETHODIMP QueryId(LPWSTR *lpId);
|
||||
STDMETHODIMP QueryAccept(const AM_MEDIA_TYPE *pmt);
|
||||
STDMETHODIMP EnumMediaTypes(IEnumMediaTypes **ppEnum);
|
||||
STDMETHODIMP QueryInternalConnections(IPin **apPin, ULONG *nPin);
|
||||
STDMETHODIMP EndOfStream();
|
||||
|
||||
STDMETHODIMP BeginFlush();
|
||||
STDMETHODIMP EndFlush();
|
||||
STDMETHODIMP NewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop,
|
||||
double dRate);
|
||||
|
||||
// IMemInputPin methods
|
||||
STDMETHODIMP GetAllocator(IMemAllocator **ppAllocator);
|
||||
STDMETHODIMP NotifyAllocator(IMemAllocator *pAllocator, BOOL bReadOnly);
|
||||
STDMETHODIMP GetAllocatorRequirements(ALLOCATOR_PROPERTIES *pProps);
|
||||
STDMETHODIMP Receive(IMediaSample *pSample);
|
||||
STDMETHODIMP ReceiveMultiple(IMediaSample **pSamples, long nSamples,
|
||||
long *nSamplesProcessed);
|
||||
STDMETHODIMP ReceiveCanBlock();
|
||||
};
|
||||
|
||||
class CaptureFilter : public IBaseFilter {
|
||||
friend class CapturePin;
|
||||
|
||||
volatile long refCount;
|
||||
FILTER_STATE state;
|
||||
ComPtr<IFilterGraph> graph;
|
||||
ComPtr<CapturePin> pin;
|
||||
|
||||
ComPtr<IAMFilterMiscFlags> misc;
|
||||
|
||||
public:
|
||||
CaptureFilter(const PinCaptureInfo &info);
|
||||
virtual ~CaptureFilter();
|
||||
|
||||
// IUnknown methods
|
||||
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
|
||||
STDMETHODIMP_(ULONG) AddRef();
|
||||
STDMETHODIMP_(ULONG) Release();
|
||||
|
||||
// IPersist method
|
||||
STDMETHODIMP GetClassID(CLSID *pClsID);
|
||||
|
||||
// IMediaFilter methods
|
||||
STDMETHODIMP GetState(DWORD dwMSecs, FILTER_STATE *State);
|
||||
STDMETHODIMP SetSyncSource(IReferenceClock *pClock);
|
||||
STDMETHODIMP GetSyncSource(IReferenceClock **pClock);
|
||||
STDMETHODIMP Stop();
|
||||
STDMETHODIMP Pause();
|
||||
STDMETHODIMP Run(REFERENCE_TIME tStart);
|
||||
|
||||
// IBaseFilter methods
|
||||
STDMETHODIMP EnumPins(IEnumPins **ppEnum);
|
||||
STDMETHODIMP FindPin(LPCWSTR Id, IPin **ppPin);
|
||||
STDMETHODIMP QueryFilterInfo(FILTER_INFO *pInfo);
|
||||
STDMETHODIMP JoinFilterGraph(IFilterGraph *pGraph, LPCWSTR pName);
|
||||
STDMETHODIMP QueryVendorInfo(LPWSTR *pVendorInfo);
|
||||
|
||||
inline CapturePin *GetPin() const { return (CapturePin *)pin; }
|
||||
};
|
||||
|
||||
class CaptureEnumPins : public IEnumPins {
|
||||
volatile long refCount = 1;
|
||||
ComPtr<CaptureFilter> filter;
|
||||
UINT curPin;
|
||||
|
||||
public:
|
||||
CaptureEnumPins(CaptureFilter *filter, CaptureEnumPins *pEnum);
|
||||
virtual ~CaptureEnumPins();
|
||||
|
||||
// IUnknown
|
||||
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
|
||||
STDMETHODIMP_(ULONG) AddRef();
|
||||
STDMETHODIMP_(ULONG) Release();
|
||||
|
||||
// IEnumPins
|
||||
STDMETHODIMP Next(ULONG cPins, IPin **ppPins, ULONG *pcFetched);
|
||||
STDMETHODIMP Skip(ULONG cPins);
|
||||
STDMETHODIMP Reset();
|
||||
STDMETHODIMP Clone(IEnumPins **ppEnum);
|
||||
};
|
||||
|
||||
class CaptureEnumMediaTypes : public IEnumMediaTypes {
|
||||
volatile long refCount = 1;
|
||||
ComPtr<CapturePin> pin;
|
||||
UINT curMT = 0;
|
||||
|
||||
public:
|
||||
CaptureEnumMediaTypes(CapturePin *pin);
|
||||
virtual ~CaptureEnumMediaTypes();
|
||||
|
||||
// IUnknown
|
||||
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
|
||||
STDMETHODIMP_(ULONG) AddRef();
|
||||
STDMETHODIMP_(ULONG) Release();
|
||||
|
||||
// IEnumMediaTypes
|
||||
STDMETHODIMP Next(ULONG cMediaTypes, AM_MEDIA_TYPE **ppMediaTypes,
|
||||
ULONG *pcFetched);
|
||||
STDMETHODIMP Skip(ULONG cMediaTypes);
|
||||
STDMETHODIMP Reset();
|
||||
STDMETHODIMP Clone(IEnumMediaTypes **ppEnum);
|
||||
};
|
||||
|
||||
}; /* namespace DShow */
|
||||
204
obs-studio/deps/libdshowcapture/src/source/device-vendor.cpp
Normal file
204
obs-studio/deps/libdshowcapture/src/source/device-vendor.cpp
Normal file
@ -0,0 +1,204 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Lain Bailey <lain@obsproject.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
#include <ElgatoUVCDevice.h>
|
||||
#include "../external/capture-device-support/SampleCode/DriverInterface.h"
|
||||
#include "device.hpp"
|
||||
#include "log.hpp"
|
||||
|
||||
#include <cinttypes>
|
||||
|
||||
#include <vidcap.h>
|
||||
|
||||
namespace DShow {
|
||||
|
||||
bool IsVendorVideoHDR(IKsPropertySet *propertySet)
|
||||
{
|
||||
EGAVDeviceProperties properties(
|
||||
propertySet, EGAVDeviceProperties::DeviceType::GC4K60SPlus);
|
||||
bool isHDR = false;
|
||||
return SUCCEEDED(properties.IsVideoHDR(isHDR)) ? isHDR : false;
|
||||
}
|
||||
|
||||
void SetVendorVideoFormat(IKsPropertySet *propertySet, bool hevcTrueAvcFalse)
|
||||
{
|
||||
EGAVDeviceProperties properties(
|
||||
propertySet, EGAVDeviceProperties::DeviceType::GC4K60SPlus);
|
||||
const HRESULT hr = properties.SetEncoderType(hevcTrueAvcFalse);
|
||||
if (SUCCEEDED(hr)) {
|
||||
Info(L"Elgato GC4K60SPlus encoder type=%ls",
|
||||
hevcTrueAvcFalse ? L"HEVC" : L"AVC");
|
||||
}
|
||||
}
|
||||
|
||||
static void SetTonemapperAvermedia(IKsPropertySet *propertySet, bool enable)
|
||||
{
|
||||
typedef struct _KSPROPERTY_AVER_HW_HDR2SDR {
|
||||
KSPROPERTY Property;
|
||||
DWORD Enable;
|
||||
} KSPROPERTY_AVER_HW_HDR2SDR, *PKSPROPERTY_AVER_HW_HDR2SDR;
|
||||
|
||||
static constexpr GUID KSPROPSETID_AVER_HDR_PROPERTY = {
|
||||
0x8A80D56F,
|
||||
0xFAC5,
|
||||
0x4692,
|
||||
{
|
||||
0xA4,
|
||||
0x16,
|
||||
0xCF,
|
||||
0x20,
|
||||
0xD4,
|
||||
0xA1,
|
||||
0x8F,
|
||||
0x47,
|
||||
},
|
||||
};
|
||||
KSPROPERTY_AVER_HW_HDR2SDR data{};
|
||||
data.Enable = enable;
|
||||
const HRESULT hr = propertySet->Set(
|
||||
KSPROPSETID_AVER_HDR_PROPERTY, 2, &data.Enable,
|
||||
sizeof(data) - sizeof(data.Property), &data, sizeof(data));
|
||||
if (SUCCEEDED(hr))
|
||||
Info(L"AVerMedia tonemapper enable=%lu", data.Enable);
|
||||
}
|
||||
|
||||
/* Special thanks to AVerMedia development team */
|
||||
static bool FindExtensionUnitNodeID(DWORD *pnNode, IBaseFilter *spCapture)
|
||||
{
|
||||
bool bFindNode = false;
|
||||
if (!spCapture)
|
||||
return bFindNode;
|
||||
|
||||
ComPtr<IKsTopologyInfo> spKsTopologyInfo = NULL;
|
||||
HRESULT hr = spCapture->QueryInterface(IID_PPV_ARGS(&spKsTopologyInfo));
|
||||
if (spKsTopologyInfo == NULL || FAILED(hr))
|
||||
return bFindNode;
|
||||
|
||||
DWORD nNodeNum = 0;
|
||||
hr = spKsTopologyInfo->get_NumNodes(&nNodeNum);
|
||||
if (FAILED(hr) || nNodeNum <= 0)
|
||||
return bFindNode;
|
||||
|
||||
GUID guidNodeType;
|
||||
for (int i = 0; i < nNodeNum; i++) {
|
||||
spKsTopologyInfo->get_NodeType(i, &guidNodeType);
|
||||
if (IsEqualGUID(guidNodeType, KSNODETYPE_DEV_SPECIFIC)) {
|
||||
*pnNode = i;
|
||||
bFindNode = true;
|
||||
}
|
||||
}
|
||||
|
||||
return bFindNode;
|
||||
}
|
||||
|
||||
static bool SetTonemapperAvermedia2(IBaseFilter *filter, bool enable)
|
||||
{
|
||||
static constexpr GUID GUID_GC553 = {
|
||||
0xC835261B,
|
||||
0xFF1C,
|
||||
0x4C9A,
|
||||
{
|
||||
0xB2,
|
||||
0xF7,
|
||||
0x93,
|
||||
0xC9,
|
||||
0x1F,
|
||||
0xCF,
|
||||
0xBE,
|
||||
0x77,
|
||||
},
|
||||
};
|
||||
|
||||
static constexpr int nId = 11;
|
||||
|
||||
ComPtr<IKsControl> spKsControl;
|
||||
HRESULT hr = filter->QueryInterface(IID_PPV_ARGS(&spKsControl));
|
||||
if (spKsControl == NULL || FAILED(hr))
|
||||
return false;
|
||||
|
||||
KSP_NODE ExtensionProp{};
|
||||
if (!FindExtensionUnitNodeID(&ExtensionProp.NodeId, filter))
|
||||
return false;
|
||||
|
||||
ExtensionProp.Property.Set = GUID_GC553;
|
||||
ExtensionProp.Property.Id = nId;
|
||||
ExtensionProp.Property.Flags = KSPROPERTY_TYPE_GET |
|
||||
KSPROPERTY_TYPE_TOPOLOGY;
|
||||
|
||||
char pData[20];
|
||||
|
||||
ULONG ulBytesReturned;
|
||||
hr = spKsControl->KsProperty(&ExtensionProp.Property,
|
||||
sizeof(ExtensionProp), pData,
|
||||
sizeof(pData), &ulBytesReturned);
|
||||
if (FAILED(hr) || (ulBytesReturned < 18))
|
||||
return false;
|
||||
|
||||
pData[15] = 0x02;
|
||||
pData[17] = enable;
|
||||
|
||||
ExtensionProp.Property.Flags = KSPROPERTY_TYPE_SET |
|
||||
KSPROPERTY_TYPE_TOPOLOGY;
|
||||
hr = spKsControl->KsProperty(&ExtensionProp.Property,
|
||||
sizeof(ExtensionProp), pData,
|
||||
sizeof(pData), &ulBytesReturned);
|
||||
const bool succeeded = SUCCEEDED(hr);
|
||||
if (succeeded)
|
||||
Info(L"AVerMedia GC553 tonemapper enable=%d", (int)enable);
|
||||
|
||||
return succeeded;
|
||||
}
|
||||
|
||||
static void SetTonemapperElgato(IKsPropertySet *propertySet, bool enable)
|
||||
{
|
||||
EGAVDeviceProperties properties(
|
||||
propertySet, EGAVDeviceProperties::DeviceType::GC4K60ProMK2);
|
||||
const HRESULT hr = properties.SetHDRTonemapping(enable);
|
||||
if (SUCCEEDED(hr)) {
|
||||
Info(L"Elgato GC4K60ProMK2 tonemapper enable=%d", (int)enable);
|
||||
} else {
|
||||
for (const EGAVDeviceID &deviceID : GetElgatoUVCDeviceIDs()) {
|
||||
std::shared_ptr<EGAVHIDInterface> hid =
|
||||
CreateEGAVHIDInterface();
|
||||
if (hid->InitHIDInterface(deviceID).Succeeded()) {
|
||||
ElgatoUVCDevice device(
|
||||
hid, IsNewDeviceType(deviceID));
|
||||
device.SetHDRTonemappingEnabled(enable);
|
||||
Info(L"Elgato UVC device (PID = 0x%04X) tonemapper enable=%d",
|
||||
deviceID.productID, (int)enable);
|
||||
hid->DeinitHIDInterface();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetVendorTonemapperUsage(IBaseFilter *filter, bool enable)
|
||||
{
|
||||
if (filter) {
|
||||
ComPtr<IKsPropertySet> propertySet =
|
||||
ComQIPtr<IKsPropertySet>(filter);
|
||||
if (propertySet) {
|
||||
SetTonemapperAvermedia(propertySet, enable);
|
||||
SetTonemapperAvermedia2(filter, enable);
|
||||
SetTonemapperElgato(propertySet, enable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace DShow */
|
||||
875
obs-studio/deps/libdshowcapture/src/source/device.cpp
Normal file
875
obs-studio/deps/libdshowcapture/src/source/device.cpp
Normal file
@ -0,0 +1,875 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Lain Bailey <lain@obsproject.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
#include "device.hpp"
|
||||
#include "dshow-device-defs.hpp"
|
||||
#include "dshow-media-type.hpp"
|
||||
#include "dshow-formats.hpp"
|
||||
#include "dshow-enum.hpp"
|
||||
#include "log.hpp"
|
||||
|
||||
#define ROCKET_WAIT_TIME_MS 5000
|
||||
|
||||
namespace DShow {
|
||||
|
||||
/* device-vendor.cpp API */
|
||||
extern bool IsVendorVideoHDR(IKsPropertySet *propertySet);
|
||||
extern void SetVendorVideoFormat(IKsPropertySet *propertySet,
|
||||
bool hevcTrueAvcFalse);
|
||||
extern void SetVendorTonemapperUsage(IBaseFilter *filter, bool enable);
|
||||
|
||||
bool SetRocketEnabled(IBaseFilter *encoder, bool enable);
|
||||
|
||||
HDevice::HDevice() : initialized(false), active(false) {}
|
||||
|
||||
HDevice::~HDevice()
|
||||
{
|
||||
if (active)
|
||||
Stop();
|
||||
|
||||
DisconnectFilters();
|
||||
|
||||
/*
|
||||
* the sleeps for the rocket are required. It seems that you cannot
|
||||
* simply start/stop the stream right away after/before you enable or
|
||||
* disable the rocket. If you start it too fast after enabling, it
|
||||
* won't return any data. If you try to turn off the rocket too
|
||||
* quickly after stopping, then it'll be perpetually stuck on, and then
|
||||
* you'll have to unplug/replug the device to get it working again.
|
||||
*/
|
||||
if (!!rocketEncoder) {
|
||||
Sleep(ROCKET_WAIT_TIME_MS);
|
||||
SetRocketEnabled(rocketEncoder, false);
|
||||
}
|
||||
}
|
||||
|
||||
bool HDevice::EnsureInitialized(const wchar_t *func)
|
||||
{
|
||||
if (!initialized) {
|
||||
Error(L"%s: context not initialized", func);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HDevice::EnsureActive(const wchar_t *func)
|
||||
{
|
||||
if (!active) {
|
||||
Error(L"%s: cannot be used while inactive", func);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HDevice::EnsureInactive(const wchar_t *func)
|
||||
{
|
||||
if (active) {
|
||||
Error(L"%s: cannot be used while active", func);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void HDevice::SendToCallback(bool video, unsigned char *data,
|
||||
size_t size, long long startTime,
|
||||
long long stopTime, long rotation)
|
||||
{
|
||||
if (!size)
|
||||
return;
|
||||
|
||||
if (video)
|
||||
videoConfig.callback(videoConfig, data, size, startTime,
|
||||
stopTime, rotation);
|
||||
else
|
||||
audioConfig.callback(audioConfig, data, size, startTime,
|
||||
stopTime);
|
||||
}
|
||||
|
||||
void HDevice::Receive(bool isVideo, IMediaSample *sample)
|
||||
{
|
||||
BYTE *ptr;
|
||||
MediaTypePtr mt;
|
||||
long roll = 0;
|
||||
bool encoded = isVideo ? ((int)videoConfig.format >= 400)
|
||||
: ((int)audioConfig.format >= 200);
|
||||
|
||||
if (!sample)
|
||||
return;
|
||||
|
||||
if (isVideo ? !videoConfig.callback : !audioConfig.callback)
|
||||
return;
|
||||
|
||||
if (reactivatePending)
|
||||
return;
|
||||
|
||||
/* auto-rotation for devices such as streamcam */
|
||||
if (isVideo && rotatableDevice) {
|
||||
ComQIPtr<IAMCameraControl> cc(videoFilter);
|
||||
if (cc) {
|
||||
long ccf = 0;
|
||||
cc->Get(CameraControl_Roll, &roll, &ccf);
|
||||
}
|
||||
}
|
||||
|
||||
if (isVideo && videoConfig.reactivateCallback) {
|
||||
ComQIPtr<IKsPropertySet> propertySet(videoFilter);
|
||||
if (propertySet) {
|
||||
const bool hdr = IsVendorVideoHDR(propertySet);
|
||||
if (deviceHdrSignal != hdr) {
|
||||
deviceHdrSignal = hdr;
|
||||
#ifdef ENABLE_HEVC
|
||||
SetVendorVideoFormat(propertySet, hdr);
|
||||
#endif
|
||||
videoConfig.reactivateCallback();
|
||||
reactivatePending = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sample->GetMediaType(&mt) == S_OK) {
|
||||
if (isVideo) {
|
||||
videoMediaType = mt;
|
||||
ConvertVideoSettings();
|
||||
} else {
|
||||
audioMediaType = mt;
|
||||
ConvertAudioSettings();
|
||||
}
|
||||
}
|
||||
|
||||
int size = sample->GetActualDataLength();
|
||||
if (!size)
|
||||
return;
|
||||
|
||||
if (FAILED(sample->GetPointer(&ptr)))
|
||||
return;
|
||||
|
||||
long long startTime, stopTime;
|
||||
bool hasTime = SUCCEEDED(sample->GetTime(&startTime, &stopTime));
|
||||
|
||||
if (encoded) {
|
||||
EncodedData &data = isVideo ? encodedVideo : encodedAudio;
|
||||
|
||||
/* packets that have time are the first packet in a group of
|
||||
* segments */
|
||||
if (hasTime) {
|
||||
SendToCallback(isVideo, data.bytes.data(),
|
||||
data.bytes.size(), data.lastStartTime,
|
||||
data.lastStopTime, roll);
|
||||
|
||||
data.bytes.resize(0);
|
||||
data.lastStartTime = startTime;
|
||||
data.lastStopTime = stopTime;
|
||||
}
|
||||
|
||||
data.bytes.insert(data.bytes.end(), (unsigned char *)ptr,
|
||||
(unsigned char *)ptr + size);
|
||||
|
||||
} else if (hasTime) {
|
||||
SendToCallback(isVideo, ptr, size, startTime, stopTime, roll);
|
||||
}
|
||||
}
|
||||
|
||||
void HDevice::ConvertVideoSettings()
|
||||
{
|
||||
VIDEOINFOHEADER *vih = (VIDEOINFOHEADER *)videoMediaType->pbFormat;
|
||||
BITMAPINFOHEADER *bmih = GetBitmapInfoHeader(videoMediaType);
|
||||
|
||||
if (bmih) {
|
||||
Debug(L"Video media type changed");
|
||||
|
||||
videoConfig.cx = bmih->biWidth;
|
||||
videoConfig.cy_abs = labs(bmih->biHeight);
|
||||
videoConfig.cy_flip = bmih->biHeight < 0;
|
||||
videoConfig.frameInterval = vih->AvgTimePerFrame;
|
||||
|
||||
bool same = videoConfig.internalFormat == videoConfig.format;
|
||||
GetMediaTypeVFormat(videoMediaType, videoConfig.internalFormat);
|
||||
|
||||
if (same)
|
||||
videoConfig.format = videoConfig.internalFormat;
|
||||
}
|
||||
}
|
||||
|
||||
void HDevice::ConvertAudioSettings()
|
||||
{
|
||||
WAVEFORMATEX *wfex =
|
||||
reinterpret_cast<WAVEFORMATEX *>(audioMediaType->pbFormat);
|
||||
|
||||
Debug(L"Audio media type changed");
|
||||
|
||||
audioConfig.sampleRate = wfex->nSamplesPerSec;
|
||||
audioConfig.channels = wfex->nChannels;
|
||||
|
||||
if (wfex->wFormatTag == WAVE_FORMAT_RAW_AAC1)
|
||||
audioConfig.format = AudioFormat::AAC;
|
||||
else if (wfex->wFormatTag == WAVE_FORMAT_DVM)
|
||||
audioConfig.format = AudioFormat::AC3;
|
||||
else if (wfex->wFormatTag == WAVE_FORMAT_MPEG)
|
||||
audioConfig.format = AudioFormat::MPGA;
|
||||
else if (wfex->wBitsPerSample == 16)
|
||||
audioConfig.format = AudioFormat::Wave16bit;
|
||||
else if (wfex->wBitsPerSample == 32)
|
||||
audioConfig.format = AudioFormat::WaveFloat;
|
||||
else
|
||||
audioConfig.format = AudioFormat::Unknown;
|
||||
}
|
||||
|
||||
#define HD_PVR1_NAME L"Hauppauge HD PVR Capture"
|
||||
|
||||
bool HDevice::SetupExceptionVideoCapture(IBaseFilter *filter,
|
||||
VideoConfig &config)
|
||||
{
|
||||
ComPtr<IPin> pin;
|
||||
|
||||
if (GetPinByName(filter, PINDIR_OUTPUT, L"656", &pin))
|
||||
return SetupEncodedVideoCapture(filter, config, HD_PVR2);
|
||||
|
||||
else if (GetPinByName(filter, PINDIR_OUTPUT, L"TS Out", &pin))
|
||||
return SetupEncodedVideoCapture(filter, config, Roxio);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool GetPinMediaType(IPin *pin, MediaType &mt)
|
||||
{
|
||||
ComPtr<IEnumMediaTypes> mediaTypes;
|
||||
|
||||
if (SUCCEEDED(pin->EnumMediaTypes(&mediaTypes))) {
|
||||
MediaTypePtr curMT;
|
||||
ULONG count = 0;
|
||||
|
||||
while (mediaTypes->Next(1, &curMT, &count) == S_OK) {
|
||||
if (curMT->formattype == FORMAT_VideoInfo) {
|
||||
mt = curMT;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HDevice::SetupVideoCapture(IBaseFilter *filter, VideoConfig &config)
|
||||
{
|
||||
ComPtr<IPin> pin;
|
||||
HRESULT hr;
|
||||
bool success;
|
||||
|
||||
if (config.name.find(L"C875") != std::string::npos ||
|
||||
config.name.find(L"Prif Streambox") != std::string::npos ||
|
||||
config.name.find(L"C835") != std::string::npos)
|
||||
return SetupEncodedVideoCapture(filter, config, AV_LGP);
|
||||
|
||||
else if (config.name.find(L"IT9910") != std::string::npos)
|
||||
return SetupEncodedVideoCapture(filter, config, HD_PVR_Rocket);
|
||||
|
||||
else if (config.name.find(HD_PVR1_NAME) != std::string::npos)
|
||||
return SetupEncodedVideoCapture(filter, config, HD_PVR1);
|
||||
|
||||
rotatableDevice = videoConfig.name.find(L"StreamCam") !=
|
||||
std::string::npos;
|
||||
|
||||
success = GetFilterPin(filter, MEDIATYPE_Video, PIN_CATEGORY_CAPTURE,
|
||||
PINDIR_OUTPUT, &pin);
|
||||
if (!success) {
|
||||
if (SetupExceptionVideoCapture(filter, config)) {
|
||||
return true;
|
||||
} else {
|
||||
Error(L"Could not get video pin");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ComQIPtr<IAMStreamConfig> pinConfig(pin);
|
||||
if (pinConfig == NULL) {
|
||||
Error(L"Could not get IAMStreamConfig for device");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (config.useDefaultConfig) {
|
||||
MediaTypePtr defaultMT;
|
||||
|
||||
hr = pinConfig->GetFormat(&defaultMT);
|
||||
if (hr == E_NOTIMPL) {
|
||||
if (!GetPinMediaType(pin, videoMediaType)) {
|
||||
Error(L"Couldn't get pin media type");
|
||||
return false;
|
||||
}
|
||||
|
||||
} else if (FAILED(hr)) {
|
||||
ErrorHR(L"Could not get default format for video", hr);
|
||||
return false;
|
||||
|
||||
} else {
|
||||
videoMediaType = defaultMT;
|
||||
}
|
||||
|
||||
ConvertVideoSettings();
|
||||
|
||||
config.format = config.internalFormat = VideoFormat::Any;
|
||||
}
|
||||
|
||||
if (!GetClosestVideoMediaType(filter, config, videoMediaType)) {
|
||||
Error(L"Could not get closest video media type");
|
||||
return false;
|
||||
}
|
||||
|
||||
hr = pinConfig->SetFormat(videoMediaType);
|
||||
if (FAILED(hr) && hr != E_NOTIMPL) {
|
||||
ErrorHR(L"Could not set video format", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
ConvertVideoSettings();
|
||||
|
||||
PinCaptureInfo info;
|
||||
info.callback = [this](IMediaSample *s) { Receive(true, s); };
|
||||
info.expectedMajorType = videoMediaType->majortype;
|
||||
|
||||
/* attempt to force intermediary filters for these types */
|
||||
if (videoConfig.format == VideoFormat::XRGB)
|
||||
info.expectedSubType = MEDIASUBTYPE_RGB32;
|
||||
else if (videoConfig.format == VideoFormat::ARGB)
|
||||
info.expectedSubType = MEDIASUBTYPE_ARGB32;
|
||||
else if (videoConfig.format == VideoFormat::RGB24)
|
||||
info.expectedSubType = MEDIASUBTYPE_RGB24;
|
||||
else if (videoConfig.format == VideoFormat::YVYU)
|
||||
info.expectedSubType = MEDIASUBTYPE_YVYU;
|
||||
else if (videoConfig.format == VideoFormat::YUY2)
|
||||
info.expectedSubType = MEDIASUBTYPE_YUY2;
|
||||
else if (videoConfig.format == VideoFormat::UYVY)
|
||||
info.expectedSubType = MEDIASUBTYPE_UYVY;
|
||||
else
|
||||
info.expectedSubType = videoMediaType->subtype;
|
||||
|
||||
videoCapture = new CaptureFilter(info);
|
||||
videoFilter = filter;
|
||||
|
||||
graph->AddFilter(videoCapture, L"Video Capture Filter");
|
||||
graph->AddFilter(videoFilter, L"Video Filter");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HDevice::SetVideoConfig(VideoConfig *config)
|
||||
{
|
||||
ComPtr<IBaseFilter> filter;
|
||||
|
||||
if (!EnsureInitialized(L"SetVideoConfig") ||
|
||||
!EnsureInactive(L"SetVideoConfig"))
|
||||
return false;
|
||||
|
||||
videoMediaType = NULL;
|
||||
graph->RemoveFilter(videoFilter);
|
||||
graph->RemoveFilter(videoCapture);
|
||||
videoFilter.Release();
|
||||
videoCapture.Release();
|
||||
|
||||
if (!config)
|
||||
return true;
|
||||
|
||||
if (config->name.empty() && config->path.empty()) {
|
||||
Error(L"No video device name or path specified");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = GetDeviceFilter(CLSID_VideoInputDeviceCategory,
|
||||
config->name.c_str(),
|
||||
config->path.c_str(), &filter);
|
||||
if (!success) {
|
||||
Error(L"Video device '%s': %s not found", config->name.c_str(),
|
||||
config->path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (filter == NULL) {
|
||||
Error(L"Could not get video filter");
|
||||
return false;
|
||||
}
|
||||
|
||||
deviceHdrSignal = false;
|
||||
reactivatePending = false;
|
||||
|
||||
ComPtr<IKsPropertySet> propertySet = ComQIPtr<IKsPropertySet>(filter);
|
||||
if (propertySet) {
|
||||
const bool hdr = IsVendorVideoHDR(propertySet);
|
||||
#ifdef ENABLE_HEVC
|
||||
SetVendorVideoFormat(propertySet, hdr);
|
||||
#endif
|
||||
deviceHdrSignal = hdr;
|
||||
}
|
||||
|
||||
videoConfig = *config;
|
||||
|
||||
if (!SetupVideoCapture(filter, videoConfig))
|
||||
return false;
|
||||
|
||||
*config = videoConfig;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HDevice::SetupExceptionAudioCapture(IPin *pin)
|
||||
{
|
||||
ComPtr<IEnumMediaTypes> enumMediaTypes;
|
||||
ULONG count = 0;
|
||||
HRESULT hr;
|
||||
MediaTypePtr mt;
|
||||
|
||||
hr = pin->EnumMediaTypes(&enumMediaTypes);
|
||||
if (FAILED(hr)) {
|
||||
WarningHR(L"SetupExceptionAudioCapture: pin->EnumMediaTypes "
|
||||
L"failed",
|
||||
hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
enumMediaTypes->Reset();
|
||||
|
||||
if (enumMediaTypes->Next(1, &mt, &count) == S_OK &&
|
||||
mt->formattype == FORMAT_WaveFormatEx) {
|
||||
audioMediaType = mt;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool is24BitAudio(AM_MEDIA_TYPE *mt)
|
||||
{
|
||||
if (mt->formattype == FORMAT_WaveFormatEx) {
|
||||
WAVEFORMATEX *wfex = (WAVEFORMATEX *)mt->pbFormat;
|
||||
return wfex->wBitsPerSample == 24;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HDevice::SetupAudioCapture(IBaseFilter *filter, AudioConfig &config)
|
||||
{
|
||||
ComPtr<IPin> pin;
|
||||
MediaTypePtr defaultMT;
|
||||
bool success;
|
||||
HRESULT hr;
|
||||
|
||||
success = GetFilterPin(filter, MEDIATYPE_Audio, PIN_CATEGORY_CAPTURE,
|
||||
PINDIR_OUTPUT, &pin);
|
||||
if (!success) {
|
||||
Error(L"Could not get audio pin");
|
||||
return false;
|
||||
}
|
||||
|
||||
ComQIPtr<IAMStreamConfig> pinConfig(pin);
|
||||
|
||||
if (config.useDefaultConfig) {
|
||||
MediaTypePtr defaultMT;
|
||||
|
||||
if (pinConfig && SUCCEEDED(pinConfig->GetFormat(&defaultMT))) {
|
||||
if (is24BitAudio(defaultMT)) {
|
||||
WAVEFORMATEX *wfex =
|
||||
(WAVEFORMATEX *)defaultMT->pbFormat;
|
||||
config.sampleRate = wfex->nSamplesPerSec;
|
||||
config.channels = wfex->nChannels;
|
||||
config.format = AudioFormat::Wave16bit;
|
||||
config.useDefaultConfig = false;
|
||||
} else {
|
||||
audioMediaType = defaultMT;
|
||||
}
|
||||
} else {
|
||||
if (!SetupExceptionAudioCapture(pin)) {
|
||||
Error(L"Could not get default format for "
|
||||
L"audio pin");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!config.useDefaultConfig) {
|
||||
if (!GetClosestAudioMediaType(filter, config, audioMediaType)) {
|
||||
Error(L"Could not get closest audio media type");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!!pinConfig) {
|
||||
hr = pinConfig->SetFormat(audioMediaType);
|
||||
|
||||
if (FAILED(hr) && hr != E_NOTIMPL) {
|
||||
Error(L"Could not set audio format");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ConvertAudioSettings();
|
||||
|
||||
PinCaptureInfo info;
|
||||
info.callback = [this](IMediaSample *s) { Receive(false, s); };
|
||||
info.expectedMajorType = audioMediaType->majortype;
|
||||
info.expectedSubType = audioMediaType->subtype;
|
||||
|
||||
audioCapture = new CaptureFilter(info);
|
||||
audioFilter = filter;
|
||||
audioConfig = config;
|
||||
|
||||
graph->AddFilter(audioCapture, L"Audio Capture Filter");
|
||||
if (!config.useVideoDevice)
|
||||
graph->AddFilter(audioFilter, L"Audio Filter");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HDevice::SetupAudioOutput(IBaseFilter *filter, AudioConfig &config)
|
||||
{
|
||||
ComPtr<IBaseFilter> outputFilter;
|
||||
const CLSID *clsID;
|
||||
HRESULT hr;
|
||||
|
||||
if (config.mode == AudioMode::WaveOut) {
|
||||
clsID = &CLSID_AudioRender;
|
||||
} else {
|
||||
clsID = &CLSID_DSoundRender;
|
||||
}
|
||||
|
||||
hr = CoCreateInstance(*clsID, nullptr, CLSCTX_INPROC_SERVER,
|
||||
IID_IBaseFilter, (void **)&outputFilter);
|
||||
if (FAILED(hr)) {
|
||||
ErrorHR(L"Failed to create audio sound output filter", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
audioFilter = filter;
|
||||
audioOutput = std::move(outputFilter);
|
||||
|
||||
graph->AddFilter(audioOutput, L"Audio Output Filter");
|
||||
if (!config.useVideoDevice)
|
||||
graph->AddFilter(audioFilter, L"Audio Filter");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HDevice::SetAudioConfig(AudioConfig *config)
|
||||
{
|
||||
ComPtr<IBaseFilter> filter;
|
||||
|
||||
if (!EnsureInitialized(L"SetAudioConfig") ||
|
||||
!EnsureInactive(L"SetAudioConfig"))
|
||||
return false;
|
||||
|
||||
if (!audioConfig.useVideoDevice)
|
||||
graph->RemoveFilter(audioFilter);
|
||||
graph->RemoveFilter(audioCapture);
|
||||
graph->RemoveFilter(audioOutput);
|
||||
audioFilter.Release();
|
||||
audioCapture.Release();
|
||||
audioOutput.Release();
|
||||
audioMediaType = NULL;
|
||||
|
||||
if (!config)
|
||||
return true;
|
||||
|
||||
if (!config->useVideoDevice && !config->useSeparateAudioFilter &&
|
||||
config->name.empty() && config->path.empty()) {
|
||||
Error(L"No audio device name or path specified");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (config->useVideoDevice) {
|
||||
if (videoFilter == NULL) {
|
||||
Error(L"Tried to use video device's built-in audio, "
|
||||
L"but no video device is present");
|
||||
return false;
|
||||
}
|
||||
|
||||
filter = videoFilter;
|
||||
} else if (config->useSeparateAudioFilter) {
|
||||
bool success =
|
||||
GetDeviceAudioFilter(videoConfig.path.c_str(), &filter);
|
||||
if (!success) {
|
||||
Error(L"Corresponding audio device for '%s' not found",
|
||||
videoConfig.path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
} else {
|
||||
bool success = GetDeviceFilter(CLSID_AudioInputDeviceCategory,
|
||||
config->name.c_str(),
|
||||
config->path.c_str(), &filter);
|
||||
if (!success) {
|
||||
Error(L"Audio device '%s': %s not found",
|
||||
config->name.c_str(), config->path.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (filter == NULL)
|
||||
return false;
|
||||
|
||||
audioConfig = *config;
|
||||
|
||||
if (config->mode == AudioMode::Capture) {
|
||||
if (!SetupAudioCapture(filter, audioConfig))
|
||||
return false;
|
||||
|
||||
*config = audioConfig;
|
||||
return true;
|
||||
}
|
||||
|
||||
return SetupAudioOutput(filter, audioConfig);
|
||||
}
|
||||
|
||||
bool HDevice::CreateGraph()
|
||||
{
|
||||
if (initialized) {
|
||||
Warning(L"Graph already created");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CreateFilterGraph(&graph, &builder, &control))
|
||||
return false;
|
||||
|
||||
initialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HDevice::FindCrossbar(IBaseFilter *filter, IBaseFilter **crossbar)
|
||||
{
|
||||
ComPtr<IPin> pin;
|
||||
REGPINMEDIUM medium;
|
||||
HRESULT hr;
|
||||
|
||||
hr = builder->FindInterface(NULL, NULL, filter, IID_IAMCrossbar,
|
||||
(void **)crossbar);
|
||||
if (SUCCEEDED(hr))
|
||||
return true;
|
||||
|
||||
if (!GetPinByName(filter, PINDIR_INPUT, nullptr, &pin))
|
||||
return false;
|
||||
if (!GetPinMedium(pin, medium))
|
||||
return false;
|
||||
if (!GetFilterByMedium(AM_KSCATEGORY_CROSSBAR, medium, crossbar))
|
||||
return false;
|
||||
|
||||
graph->AddFilter(*crossbar, L"Crossbar Filter");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HDevice::ConnectPins(const GUID &category, const GUID &type,
|
||||
IBaseFilter *filter, IBaseFilter *capture)
|
||||
{
|
||||
HRESULT hr;
|
||||
ComPtr<IBaseFilter> crossbar;
|
||||
ComPtr<IPin> filterPin;
|
||||
ComPtr<IPin> capturePin;
|
||||
bool connectCrossbar = !encodedDevice && type == MEDIATYPE_Video;
|
||||
|
||||
if (!EnsureInitialized(L"HDevice::ConnectPins") ||
|
||||
!EnsureInactive(L"HDevice::ConnectPins"))
|
||||
return false;
|
||||
|
||||
if (connectCrossbar && FindCrossbar(filter, &crossbar)) {
|
||||
if (!DirectConnectFilters(graph, crossbar, filter)) {
|
||||
Warning(L"HDevice::ConnectPins: Failed to connect "
|
||||
L"crossbar");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!GetFilterPin(filter, type, category, PINDIR_OUTPUT, &filterPin)) {
|
||||
Error(L"HDevice::ConnectPins: Failed to find pin");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!GetPinByName(capture, PINDIR_INPUT, nullptr, &capturePin)) {
|
||||
Error(L"HDevice::ConnectPins: Failed to find capture pin");
|
||||
return false;
|
||||
}
|
||||
|
||||
hr = graph->ConnectDirect(filterPin, capturePin, nullptr);
|
||||
if (FAILED(hr)) {
|
||||
WarningHR(L"HDevice::ConnectPins: failed to connect pins", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HDevice::RenderFilters(const GUID &category, const GUID &type,
|
||||
IBaseFilter *filter, IBaseFilter *capture)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
if (!EnsureInitialized(L"HDevice::RenderFilters") ||
|
||||
!EnsureInactive(L"HDevice::RenderFilters"))
|
||||
return false;
|
||||
|
||||
hr = builder->RenderStream(&category, &type, filter, NULL, capture);
|
||||
if (FAILED(hr)) {
|
||||
WarningHR(L"HDevice::ConnectFilters: RenderStream failed", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void HDevice::SetAudioBuffering(int bufferingMs)
|
||||
{
|
||||
ComPtr<IPin> pin;
|
||||
bool success = GetFilterPin(audioFilter, MEDIATYPE_Audio,
|
||||
PIN_CATEGORY_CAPTURE, PINDIR_OUTPUT, &pin);
|
||||
if (!success)
|
||||
return;
|
||||
|
||||
ComQIPtr<IAMStreamConfig> config(pin);
|
||||
if (!config)
|
||||
return;
|
||||
|
||||
ComQIPtr<IAMBufferNegotiation> neg(pin);
|
||||
if (!neg)
|
||||
return;
|
||||
|
||||
MediaTypePtr mt;
|
||||
if (FAILED(config->GetFormat(&mt)))
|
||||
return;
|
||||
|
||||
if (mt->formattype != FORMAT_WaveFormatEx)
|
||||
return;
|
||||
if (mt->cbFormat != sizeof(WAVEFORMATEX))
|
||||
return;
|
||||
|
||||
WAVEFORMATEX *wfex = (WAVEFORMATEX *)mt->pbFormat;
|
||||
|
||||
ALLOCATOR_PROPERTIES props;
|
||||
props.cBuffers = -1;
|
||||
props.cbBuffer = wfex->nAvgBytesPerSec * bufferingMs / 1000;
|
||||
props.cbAlign = -1;
|
||||
props.cbPrefix = -1;
|
||||
HRESULT hr = neg->SuggestAllocatorProperties(&props);
|
||||
if (FAILED(hr))
|
||||
WarningHR(L"Could not set allocator properties on audio "
|
||||
L"capture pin",
|
||||
hr);
|
||||
}
|
||||
|
||||
bool HDevice::ConnectFilters()
|
||||
{
|
||||
bool success = true;
|
||||
|
||||
if (!EnsureInitialized(L"ConnectFilters") ||
|
||||
!EnsureInactive(L"ConnectFilters"))
|
||||
return false;
|
||||
|
||||
if (videoCapture != NULL) {
|
||||
/* use hardware tonemapper for narrow format (SDR), not wide (HDR) */
|
||||
const bool enable_tonemapper = videoConfig.format !=
|
||||
VideoFormat::P010;
|
||||
SetVendorTonemapperUsage(videoFilter, enable_tonemapper);
|
||||
|
||||
success = ConnectPins(PIN_CATEGORY_CAPTURE, MEDIATYPE_Video,
|
||||
videoFilter, videoCapture);
|
||||
if (!success) {
|
||||
success = RenderFilters(PIN_CATEGORY_CAPTURE,
|
||||
MEDIATYPE_Video, videoFilter,
|
||||
videoCapture);
|
||||
}
|
||||
}
|
||||
|
||||
if ((audioCapture || audioOutput) && success) {
|
||||
IBaseFilter *filter = (audioCapture != nullptr)
|
||||
? audioCapture.Get()
|
||||
: audioOutput.Get();
|
||||
|
||||
/* Stream engine has a bug where it will break if you try to
|
||||
* set different audio buffering, so don't use audio buffering
|
||||
* if using the stream engine's audio */
|
||||
bool streamEngine = audioConfig.useVideoDevice &&
|
||||
(videoConfig.name.find(L"Stream Engine") !=
|
||||
std::string::npos);
|
||||
|
||||
if (!streamEngine && audioCapture != nullptr)
|
||||
SetAudioBuffering(audioConfig.buffer ? audioConfig.buffer : 10);
|
||||
|
||||
success = ConnectPins(PIN_CATEGORY_CAPTURE, MEDIATYPE_Audio,
|
||||
audioFilter, filter);
|
||||
if (!success) {
|
||||
success = RenderFilters(PIN_CATEGORY_CAPTURE,
|
||||
MEDIATYPE_Audio, audioFilter,
|
||||
filter);
|
||||
}
|
||||
}
|
||||
|
||||
if (success)
|
||||
LogFilters(graph);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void HDevice::DisconnectFilters()
|
||||
{
|
||||
ComPtr<IEnumFilters> filterEnum;
|
||||
HRESULT hr;
|
||||
|
||||
if (!graph)
|
||||
return;
|
||||
|
||||
hr = graph->EnumFilters(&filterEnum);
|
||||
if (FAILED(hr))
|
||||
return;
|
||||
|
||||
ComPtr<IBaseFilter> filter;
|
||||
while (filterEnum->Next(1, &filter, nullptr) == S_OK) {
|
||||
graph->RemoveFilter(filter);
|
||||
filterEnum->Reset();
|
||||
}
|
||||
}
|
||||
|
||||
Result HDevice::Start()
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
if (!EnsureInitialized(L"Start") || !EnsureInactive(L"Start"))
|
||||
return Result::Error;
|
||||
|
||||
if (!!rocketEncoder)
|
||||
Sleep(ROCKET_WAIT_TIME_MS);
|
||||
|
||||
hr = control->Run();
|
||||
|
||||
if (FAILED(hr)) {
|
||||
if (hr == (HRESULT)0x8007001F) {
|
||||
WarningHR(L"Run failed, device already in use", hr);
|
||||
return Result::InUse;
|
||||
} else {
|
||||
WarningHR(L"Run failed", hr);
|
||||
return Result::Error;
|
||||
}
|
||||
}
|
||||
|
||||
active = true;
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
void HDevice::Stop()
|
||||
{
|
||||
if (active) {
|
||||
control->Stop();
|
||||
active = false;
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace DShow */
|
||||
120
obs-studio/deps/libdshowcapture/src/source/device.hpp
Normal file
120
obs-studio/deps/libdshowcapture/src/source/device.hpp
Normal file
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Lain Bailey <lain@obsproject.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../dshowcapture.hpp"
|
||||
#include "capture-filter.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
using namespace std;
|
||||
|
||||
namespace DShow {
|
||||
|
||||
struct EncodedData {
|
||||
long long lastStartTime = 0;
|
||||
long long lastStopTime = 0;
|
||||
vector<unsigned char> bytes;
|
||||
};
|
||||
|
||||
struct EncodedDevice {
|
||||
VideoFormat videoFormat;
|
||||
ULONG videoPacketID;
|
||||
long width;
|
||||
long height;
|
||||
long long frameInterval;
|
||||
|
||||
AudioFormat audioFormat;
|
||||
ULONG audioPacketID;
|
||||
DWORD samplesPerSec;
|
||||
};
|
||||
|
||||
struct HDevice {
|
||||
ComPtr<IGraphBuilder> graph;
|
||||
ComPtr<ICaptureGraphBuilder2> builder;
|
||||
ComPtr<IMediaControl> control;
|
||||
|
||||
ComPtr<IBaseFilter> videoFilter;
|
||||
ComPtr<IBaseFilter> audioFilter;
|
||||
ComPtr<CaptureFilter> videoCapture;
|
||||
ComPtr<CaptureFilter> audioCapture;
|
||||
ComPtr<IBaseFilter> audioOutput;
|
||||
ComPtr<IBaseFilter> rocketEncoder;
|
||||
MediaType videoMediaType;
|
||||
MediaType audioMediaType;
|
||||
VideoConfig videoConfig;
|
||||
AudioConfig audioConfig;
|
||||
|
||||
bool encodedDevice = false;
|
||||
bool rotatableDevice = false;
|
||||
bool deviceHdrSignal = false;
|
||||
bool reactivatePending = false;
|
||||
bool initialized;
|
||||
bool active;
|
||||
|
||||
EncodedData encodedVideo;
|
||||
EncodedData encodedAudio;
|
||||
|
||||
HDevice();
|
||||
~HDevice();
|
||||
|
||||
void ConvertVideoSettings();
|
||||
void ConvertAudioSettings();
|
||||
|
||||
bool EnsureInitialized(const wchar_t *func);
|
||||
bool EnsureActive(const wchar_t *func);
|
||||
bool EnsureInactive(const wchar_t *func);
|
||||
|
||||
inline void SendToCallback(bool video, unsigned char *data, size_t size,
|
||||
long long startTime, long long stopTime,
|
||||
long rotation);
|
||||
|
||||
void Receive(bool video, IMediaSample *sample);
|
||||
|
||||
bool SetupEncodedVideoCapture(IBaseFilter *filter, VideoConfig &config,
|
||||
const EncodedDevice &info);
|
||||
|
||||
bool SetupExceptionVideoCapture(IBaseFilter *filter,
|
||||
VideoConfig &config);
|
||||
|
||||
bool SetupExceptionAudioCapture(IPin *pin);
|
||||
|
||||
bool SetupVideoCapture(IBaseFilter *filter, VideoConfig &config);
|
||||
bool SetupAudioCapture(IBaseFilter *filter, AudioConfig &config);
|
||||
|
||||
bool SetupAudioOutput(IBaseFilter *filter, AudioConfig &config);
|
||||
|
||||
bool SetVideoConfig(VideoConfig *config);
|
||||
bool SetAudioConfig(AudioConfig *config);
|
||||
|
||||
bool CreateGraph();
|
||||
bool FindCrossbar(IBaseFilter *filter, IBaseFilter **crossbar);
|
||||
bool ConnectPins(const GUID &category, const GUID &type,
|
||||
IBaseFilter *filter, IBaseFilter *capture);
|
||||
bool RenderFilters(const GUID &category, const GUID &type,
|
||||
IBaseFilter *filter, IBaseFilter *capture);
|
||||
void SetAudioBuffering(int bufferingMs);
|
||||
bool ConnectFilters();
|
||||
void DisconnectFilters();
|
||||
Result Start();
|
||||
void Stop();
|
||||
};
|
||||
|
||||
}; /* namespace DShow */
|
||||
1031
obs-studio/deps/libdshowcapture/src/source/dshow-base.cpp
Normal file
1031
obs-studio/deps/libdshowcapture/src/source/dshow-base.cpp
Normal file
File diff suppressed because it is too large
Load Diff
80
obs-studio/deps/libdshowcapture/src/source/dshow-base.hpp
Normal file
80
obs-studio/deps/libdshowcapture/src/source/dshow-base.hpp
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Lain Bailey <lain@obsproject.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define WIN32_MEAN_AND_LEAN
|
||||
#define __STREAMS__
|
||||
#include <windows.h>
|
||||
#include <dshow.h>
|
||||
#include <ks.h>
|
||||
#include <ksmedia.h>
|
||||
#include <ksproxy.h>
|
||||
#include <Amaudio.h>
|
||||
#include <Dvdmedia.h>
|
||||
|
||||
#include "ComPtr.hpp"
|
||||
#include "CoTaskMemPtr.hpp"
|
||||
|
||||
#include <string>
|
||||
using namespace std;
|
||||
|
||||
#define DSHOW_UNUSED(param) (void)param;
|
||||
|
||||
namespace DShow {
|
||||
|
||||
bool CreateFilterGraph(IGraphBuilder **graph, ICaptureGraphBuilder2 **builder,
|
||||
IMediaControl **control);
|
||||
|
||||
void LogFilters(IGraphBuilder *graph);
|
||||
|
||||
bool GetDeviceFilter(const IID &type, const wchar_t *name, const wchar_t *path,
|
||||
IBaseFilter **filter);
|
||||
|
||||
bool GetFilterPin(IBaseFilter *filter, const GUID &type, const GUID &category,
|
||||
PIN_DIRECTION dir, IPin **pin);
|
||||
|
||||
bool GetPinByName(IBaseFilter *filter, PIN_DIRECTION dir, const wchar_t *name,
|
||||
IPin **pin);
|
||||
|
||||
bool GetPinByMedium(IBaseFilter *filter, REGPINMEDIUM &medium, IPin **pin);
|
||||
bool GetFilterByMedium(const CLSID &id, REGPINMEDIUM &medium,
|
||||
IBaseFilter **filter);
|
||||
|
||||
bool GetPinMedium(IPin *pin, REGPINMEDIUM &medium);
|
||||
|
||||
bool DirectConnectFilters(IFilterGraph *graph, IBaseFilter *filterOut,
|
||||
IBaseFilter *filterIn);
|
||||
|
||||
/**
|
||||
* This maps a created demuxer pin to a packet ID for the mux stream. Note
|
||||
* that this needs to be called after the device filters are connected to the
|
||||
* demux filter.
|
||||
*/
|
||||
HRESULT MapPinToPacketID(IPin *pin, ULONG packetID);
|
||||
|
||||
wstring ConvertHRToEnglish(HRESULT hr);
|
||||
|
||||
/**
|
||||
* Get audio filter for the same device as the given video device path
|
||||
*/
|
||||
bool GetDeviceAudioFilter(const wchar_t *videoDevicePath,
|
||||
IBaseFilter **audioCaptureFilter);
|
||||
|
||||
}; /* namespace DShow */
|
||||
156
obs-studio/deps/libdshowcapture/src/source/dshow-demux.cpp
Normal file
156
obs-studio/deps/libdshowcapture/src/source/dshow-demux.cpp
Normal file
@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Lain Bailey <lain@obsproject.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
#include "dshow-demux.hpp"
|
||||
#include "dshow-formats.hpp"
|
||||
#include "log.hpp"
|
||||
|
||||
namespace DShow {
|
||||
|
||||
static inline DWORD VideoFormatToFourCC(VideoFormat format)
|
||||
{
|
||||
if (format == VideoFormat::H264)
|
||||
return MAKEFOURCC('H', '2', '6', '4');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline const GUID &VideoFormatToSubType(VideoFormat format)
|
||||
{
|
||||
if (format == VideoFormat::H264)
|
||||
return MEDIASUBTYPE_H264;
|
||||
|
||||
return GUID_NULL;
|
||||
}
|
||||
|
||||
bool CreateDemuxVideoPin(IBaseFilter *demuxFilter, MediaType &mt, long width,
|
||||
long height, long long frameTime, VideoFormat format)
|
||||
{
|
||||
ComQIPtr<IMpeg2Demultiplexer> demuxer(demuxFilter);
|
||||
if (!demuxer) {
|
||||
Warning(L"CreateDemuxVideoPin: Failed to get "
|
||||
L"IMpeg2Demultiplexer from filter");
|
||||
return false;
|
||||
}
|
||||
|
||||
ComPtr<IPin> pin;
|
||||
HRESULT hr;
|
||||
|
||||
VIDEOINFOHEADER *vih = mt.AllocFormat<VIDEOINFOHEADER>();
|
||||
vih->bmiHeader.biSize = sizeof(vih->bmiHeader);
|
||||
vih->bmiHeader.biWidth = width;
|
||||
vih->bmiHeader.biHeight = height;
|
||||
vih->bmiHeader.biCompression = VideoFormatToFourCC(format);
|
||||
vih->rcSource.right = width;
|
||||
vih->rcSource.bottom = height;
|
||||
vih->AvgTimePerFrame = frameTime;
|
||||
|
||||
if (!vih->bmiHeader.biCompression) {
|
||||
Warning(L"CreateDemuxVideoPin: Invalid video format");
|
||||
return false;
|
||||
}
|
||||
|
||||
mt->majortype = MEDIATYPE_Video;
|
||||
mt->subtype = VideoFormatToSubType(format);
|
||||
mt->formattype = FORMAT_VideoInfo;
|
||||
mt->bTemporalCompression = true;
|
||||
|
||||
wchar_t *name = (wchar_t *)CoTaskMemAlloc(sizeof(DEMUX_VIDEO_PIN));
|
||||
memcpy(name, DEMUX_VIDEO_PIN, sizeof(DEMUX_VIDEO_PIN));
|
||||
|
||||
hr = demuxer->CreateOutputPin(mt, name, &pin);
|
||||
if (FAILED(hr)) {
|
||||
WarningHR(L"CreateDemuxVideoPin: Failed to create video pin "
|
||||
L"on demuxer",
|
||||
hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline WORD AudioFormatToFormatTag(AudioFormat format)
|
||||
{
|
||||
if (format == AudioFormat::AAC)
|
||||
return WAVE_FORMAT_RAW_AAC1;
|
||||
else if (format == AudioFormat::AC3)
|
||||
return WAVE_FORMAT_DVM;
|
||||
else if (format == AudioFormat::MPGA)
|
||||
return WAVE_FORMAT_MPEG;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline const GUID &AudioFormatToSubType(AudioFormat format)
|
||||
{
|
||||
if (format == AudioFormat::AAC)
|
||||
return MEDIASUBTYPE_RAW_AAC1;
|
||||
else if (format == AudioFormat::AC3)
|
||||
return MEDIASUBTYPE_DVM;
|
||||
else if (format == AudioFormat::MPGA)
|
||||
return MEDIASUBTYPE_MPEG1AudioPayload;
|
||||
|
||||
return GUID_NULL;
|
||||
}
|
||||
|
||||
bool CreateDemuxAudioPin(IBaseFilter *demuxFilter, MediaType &mt,
|
||||
DWORD samplesPerSec, WORD bitsPerSample, WORD channels,
|
||||
AudioFormat format)
|
||||
{
|
||||
ComQIPtr<IMpeg2Demultiplexer> demuxer(demuxFilter);
|
||||
if (!demuxer) {
|
||||
Warning(L"CreateDemuxAudioPin: Failed to get "
|
||||
L"IMpeg2Demultiplexer from filter");
|
||||
return false;
|
||||
}
|
||||
|
||||
ComPtr<IPin> pin;
|
||||
HRESULT hr;
|
||||
|
||||
WAVEFORMATEX *wfex = mt.AllocFormat<WAVEFORMATEX>();
|
||||
wfex->wFormatTag = AudioFormatToFormatTag(format);
|
||||
wfex->nChannels = channels;
|
||||
wfex->nSamplesPerSec = samplesPerSec;
|
||||
wfex->wBitsPerSample = bitsPerSample;
|
||||
|
||||
if (!wfex->wFormatTag) {
|
||||
Warning(L"CreateDemuxAudioPin: Invalid audio format");
|
||||
return false;
|
||||
}
|
||||
|
||||
mt->majortype = MEDIATYPE_Audio;
|
||||
mt->subtype = AudioFormatToSubType(format);
|
||||
mt->formattype = FORMAT_WaveFormatEx;
|
||||
mt->bTemporalCompression = true;
|
||||
|
||||
wchar_t *name = (wchar_t *)CoTaskMemAlloc(sizeof(DEMUX_AUDIO_PIN));
|
||||
memcpy(name, DEMUX_AUDIO_PIN, sizeof(DEMUX_AUDIO_PIN));
|
||||
|
||||
hr = demuxer->CreateOutputPin(mt, name, &pin);
|
||||
if (FAILED(hr)) {
|
||||
WarningHR(L"CreateDemuxAudioPin: Failed to create audio pin "
|
||||
L"on demuxer",
|
||||
hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}; /* namespace DShow */
|
||||
38
obs-studio/deps/libdshowcapture/src/source/dshow-demux.hpp
Normal file
38
obs-studio/deps/libdshowcapture/src/source/dshow-demux.hpp
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Lain Bailey <lain@obsproject.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../dshowcapture.hpp"
|
||||
#include "dshow-base.hpp"
|
||||
#include "dshow-media-type.hpp"
|
||||
|
||||
namespace DShow {
|
||||
|
||||
#define DEMUX_VIDEO_PIN L"Demuxer Video Pin"
|
||||
#define DEMUX_AUDIO_PIN L"Demuxer Audio Pin"
|
||||
|
||||
bool CreateDemuxVideoPin(IBaseFilter *demuxFilter, MediaType &mt, long width,
|
||||
long height, long long frameTime, VideoFormat format);
|
||||
|
||||
bool CreateDemuxAudioPin(IBaseFilter *demuxFilter, MediaType &mt,
|
||||
DWORD samplesPerSec, WORD bitsPerSample, WORD channels,
|
||||
AudioFormat format);
|
||||
|
||||
}; /* namespace DShow */
|
||||
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Lain Bailey <lain@obsproject.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace DShow {
|
||||
|
||||
#define COMMON_ENCODED_CX 720
|
||||
#define COMMON_ENCODED_CY 480
|
||||
#define COMMON_ENCODED_INTERVAL (10010000000LL / 60000LL)
|
||||
#define COMMON_ENCODED_VFORMAT VideoFormat::H264
|
||||
#define COMMON_ENCODED_SAMPLERATE 48000
|
||||
|
||||
static const EncodedDevice HD_PVR1 = {COMMON_ENCODED_VFORMAT,
|
||||
0x1011UL,
|
||||
COMMON_ENCODED_CX,
|
||||
COMMON_ENCODED_CY,
|
||||
COMMON_ENCODED_INTERVAL,
|
||||
AudioFormat::AC3,
|
||||
0x1100UL,
|
||||
COMMON_ENCODED_SAMPLERATE};
|
||||
|
||||
static const EncodedDevice HD_PVR2 = {COMMON_ENCODED_VFORMAT,
|
||||
0x1011UL,
|
||||
COMMON_ENCODED_CX,
|
||||
COMMON_ENCODED_CY,
|
||||
COMMON_ENCODED_INTERVAL,
|
||||
AudioFormat::AAC,
|
||||
0x1100UL,
|
||||
COMMON_ENCODED_SAMPLERATE};
|
||||
|
||||
static const EncodedDevice Roxio = {
|
||||
COMMON_ENCODED_VFORMAT,
|
||||
0x1011UL,
|
||||
COMMON_ENCODED_CX,
|
||||
COMMON_ENCODED_CY,
|
||||
COMMON_ENCODED_INTERVAL,
|
||||
AudioFormat::AAC,
|
||||
0x010FUL,
|
||||
COMMON_ENCODED_SAMPLERATE,
|
||||
};
|
||||
|
||||
static const EncodedDevice HD_PVR_Rocket = {COMMON_ENCODED_VFORMAT,
|
||||
0x07D1UL,
|
||||
COMMON_ENCODED_CX,
|
||||
COMMON_ENCODED_CY,
|
||||
COMMON_ENCODED_INTERVAL,
|
||||
AudioFormat::AAC,
|
||||
0x07D2UL,
|
||||
COMMON_ENCODED_SAMPLERATE};
|
||||
|
||||
static const EncodedDevice AV_LGP = {COMMON_ENCODED_VFORMAT,
|
||||
68,
|
||||
COMMON_ENCODED_CX,
|
||||
COMMON_ENCODED_CY,
|
||||
COMMON_ENCODED_INTERVAL,
|
||||
AudioFormat::AAC,
|
||||
69,
|
||||
COMMON_ENCODED_SAMPLERATE};
|
||||
|
||||
}; /* namespace DShow */
|
||||
@ -0,0 +1,260 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Lain Bailey <lain@obsproject.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
#include "dshow-base.hpp"
|
||||
#include "dshow-media-type.hpp"
|
||||
#include "dshow-formats.hpp"
|
||||
#include "dshow-demux.hpp"
|
||||
#include "capture-filter.hpp"
|
||||
#include "device.hpp"
|
||||
#include "log.hpp"
|
||||
|
||||
namespace DShow {
|
||||
|
||||
static inline bool CreateFilters(IBaseFilter *filter, IBaseFilter **crossbar,
|
||||
IBaseFilter **encoder, IBaseFilter **demuxer)
|
||||
{
|
||||
ComPtr<IPin> inputPin;
|
||||
ComPtr<IPin> outputPin;
|
||||
REGPINMEDIUM inMedium;
|
||||
REGPINMEDIUM outMedium;
|
||||
bool hasOutMedium;
|
||||
HRESULT hr;
|
||||
|
||||
if (!GetPinByName(filter, PINDIR_INPUT, nullptr, &inputPin)) {
|
||||
Warning(L"Encoded Device: Failed to get input pin");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!GetPinByName(filter, PINDIR_OUTPUT, nullptr, &outputPin)) {
|
||||
Warning(L"Encoded Device: Failed to get output pin");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!GetPinMedium(inputPin, inMedium)) {
|
||||
Warning(L"Encoded Device: Failed to get input pin medium");
|
||||
return false;
|
||||
}
|
||||
|
||||
hasOutMedium = GetPinMedium(outputPin, outMedium);
|
||||
|
||||
if (!GetFilterByMedium(AM_KSCATEGORY_CROSSBAR, inMedium, crossbar)) {
|
||||
Warning(L"Encoded Device: Failed to get crossbar filter");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* perfectly okay if there's no encoder filter, some don't have them */
|
||||
if (hasOutMedium)
|
||||
GetFilterByMedium(KSCATEGORY_ENCODER, outMedium, encoder);
|
||||
|
||||
hr = CoCreateInstance(CLSID_MPEG2Demultiplexer, nullptr,
|
||||
CLSCTX_INPROC_SERVER, IID_IBaseFilter,
|
||||
(void **)demuxer);
|
||||
if (FAILED(hr)) {
|
||||
WarningHR(L"Encoded Device: Failed to create demuxer", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool ConnectEncodedFilters(IGraphBuilder *graph,
|
||||
IBaseFilter *filter,
|
||||
IBaseFilter *crossbar,
|
||||
IBaseFilter *encoder,
|
||||
IBaseFilter *demuxer)
|
||||
{
|
||||
if (!DirectConnectFilters(graph, crossbar, filter)) {
|
||||
Warning(L"Encoded Device: Failed to connect crossbar to "
|
||||
L"device");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!!encoder) {
|
||||
if (!DirectConnectFilters(graph, filter, encoder)) {
|
||||
Warning(L"Encoded Device: Failed to connect device to "
|
||||
L"encoder");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!DirectConnectFilters(graph, encoder, demuxer)) {
|
||||
Warning(L"Encoded Device: Failed to connect encoder to "
|
||||
L"demuxer");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!DirectConnectFilters(graph, filter, demuxer)) {
|
||||
Warning(L"Encoded Device: Failed to connect device to "
|
||||
L"demuxer");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool MapPacketIDs(IBaseFilter *demuxer, ULONG video, ULONG audio)
|
||||
{
|
||||
ComPtr<IPin> videoPin, audioPin;
|
||||
HRESULT hr;
|
||||
|
||||
if (!GetPinByName(demuxer, PINDIR_OUTPUT, DEMUX_VIDEO_PIN, &videoPin)) {
|
||||
Warning(L"Encoded Device: Could not get video pin from "
|
||||
L"demuxer");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!GetPinByName(demuxer, PINDIR_OUTPUT, DEMUX_AUDIO_PIN, &audioPin)) {
|
||||
Warning(L"Encoded Device: Could not get audio pin from "
|
||||
L"demuxer");
|
||||
return false;
|
||||
}
|
||||
|
||||
hr = MapPinToPacketID(videoPin, video);
|
||||
if (FAILED(hr)) {
|
||||
WarningHR(L"Encoded Device: Failed to map demuxer video pin "
|
||||
L"packet ID",
|
||||
hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
hr = MapPinToPacketID(audioPin, audio);
|
||||
if (FAILED(hr)) {
|
||||
WarningHR(L"Encoded Device: Failed to map demuxer audio pin "
|
||||
L"packet ID",
|
||||
hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* rocket-specific workaround code. I have no idea what any of these numbers
|
||||
* are except the GUID which was obvious. All I know is calling
|
||||
* IKsPropertySet::Set turns on/off some sort of 'mode' on the device.
|
||||
* I discovered this merely by chance while monitoring API usage in other
|
||||
* programs because I could not figure out how the hell to get this thing
|
||||
* to turn on.
|
||||
*/
|
||||
static const GUID RocketEncoderGUID = {0x99100000,
|
||||
0xa330,
|
||||
0x11e1,
|
||||
{0xa3, 0x80, 0x99, 0x10, 0x68, 0x64,
|
||||
0x00, 0x00}};
|
||||
|
||||
struct RocketPropStruct {
|
||||
DWORD dwSize;
|
||||
DWORD unknown1;
|
||||
DWORD unknown2;
|
||||
DWORD unknown3;
|
||||
DWORD code;
|
||||
DWORD unknown4;
|
||||
BOOL enabled;
|
||||
};
|
||||
|
||||
struct RocketInstStruct {
|
||||
DWORD code;
|
||||
DWORD unknown1;
|
||||
};
|
||||
|
||||
bool SetRocketEnabled(IBaseFilter *encoder, bool enable)
|
||||
{
|
||||
static const ULONG rocketEnableId = 0x9910E001;
|
||||
static const DWORD rocketEnableCode = 0x38384001;
|
||||
RocketInstStruct rocketInstance = {};
|
||||
RocketPropStruct rocketProperty = {};
|
||||
|
||||
ComQIPtr<IKsPropertySet> propertySet(encoder);
|
||||
if (!propertySet)
|
||||
return false;
|
||||
|
||||
rocketProperty.dwSize = sizeof(rocketProperty);
|
||||
rocketProperty.code = rocketEnableCode;
|
||||
rocketProperty.enabled = enable;
|
||||
rocketInstance.code = rocketEnableCode;
|
||||
|
||||
HRESULT hr = propertySet->Set(RocketEncoderGUID, rocketEnableId,
|
||||
&rocketInstance, sizeof(rocketInstance),
|
||||
&rocketProperty, sizeof(rocketProperty));
|
||||
|
||||
return SUCCEEDED(hr);
|
||||
}
|
||||
|
||||
bool HDevice::SetupEncodedVideoCapture(IBaseFilter *filter, VideoConfig &config,
|
||||
const EncodedDevice &info)
|
||||
{
|
||||
ComPtr<IBaseFilter> crossbar;
|
||||
ComPtr<IBaseFilter> encoder;
|
||||
ComPtr<IBaseFilter> demuxer;
|
||||
MediaType mtVideo;
|
||||
MediaType mtAudio;
|
||||
|
||||
if (!CreateFilters(filter, &crossbar, &encoder, &demuxer))
|
||||
return false;
|
||||
|
||||
if (!CreateDemuxVideoPin(demuxer, mtVideo, info.width, info.height,
|
||||
info.frameInterval, info.videoFormat))
|
||||
return false;
|
||||
|
||||
if (!CreateDemuxAudioPin(demuxer, mtAudio, info.samplesPerSec, 16, 2,
|
||||
info.audioFormat))
|
||||
return false;
|
||||
|
||||
config.cx = info.width;
|
||||
config.cy_abs = labs(info.height);
|
||||
config.cy_flip = info.height < 0;
|
||||
config.frameInterval = info.frameInterval;
|
||||
config.format = info.videoFormat;
|
||||
config.internalFormat = info.videoFormat;
|
||||
|
||||
PinCaptureInfo pci;
|
||||
pci.callback = [this](IMediaSample *s) { Receive(true, s); };
|
||||
pci.expectedMajorType = mtVideo->majortype;
|
||||
pci.expectedSubType = mtVideo->subtype;
|
||||
|
||||
videoCapture = new CaptureFilter(pci);
|
||||
videoFilter = demuxer;
|
||||
|
||||
if (!!encoder && config.name.find(L"IT9910") != std::string::npos) {
|
||||
rocketEncoder = encoder;
|
||||
|
||||
if (!SetRocketEnabled(rocketEncoder, true))
|
||||
return false;
|
||||
}
|
||||
|
||||
graph->AddFilter(crossbar, L"Crossbar");
|
||||
graph->AddFilter(filter, L"Device");
|
||||
graph->AddFilter(demuxer, L"Demuxer");
|
||||
graph->AddFilter(videoCapture, L"Capture Filter");
|
||||
|
||||
if (!!encoder)
|
||||
graph->AddFilter(encoder, L"Encoder");
|
||||
|
||||
bool success = ConnectEncodedFilters(graph, filter, crossbar, encoder,
|
||||
demuxer);
|
||||
if (success)
|
||||
success = MapPacketIDs(demuxer, info.videoPacketID,
|
||||
info.audioPacketID);
|
||||
|
||||
encodedDevice = success;
|
||||
return success;
|
||||
}
|
||||
|
||||
}; /* namespace DShow */
|
||||
573
obs-studio/deps/libdshowcapture/src/source/dshow-enum.cpp
Normal file
573
obs-studio/deps/libdshowcapture/src/source/dshow-enum.cpp
Normal file
@ -0,0 +1,573 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Lain Bailey <lain@obsproject.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <mutex>
|
||||
#include "dshow-enum.hpp"
|
||||
#include "dshow-formats.hpp"
|
||||
#include "log.hpp"
|
||||
|
||||
#undef DEFINE_GUID
|
||||
#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
|
||||
EXTERN_C const GUID DECLSPEC_SELECTANY name = { \
|
||||
l, w1, w2, {b1, b2, b3, b4, b5, b6, b7, b8}}
|
||||
|
||||
#include "external/IVideoCaptureFilter.h"
|
||||
|
||||
namespace DShow {
|
||||
using namespace std;
|
||||
|
||||
typedef bool (*EnumCapsCallback)(void *param, const AM_MEDIA_TYPE &mt,
|
||||
const BYTE *data);
|
||||
|
||||
static void EnumElgatoCaps(IPin *pin, EnumCapsCallback callback, void *param)
|
||||
{
|
||||
ComPtr<IEnumMediaTypes> mediaTypes;
|
||||
|
||||
if (SUCCEEDED(pin->EnumMediaTypes(&mediaTypes))) {
|
||||
MediaTypePtr mt;
|
||||
ULONG count = 0;
|
||||
|
||||
while (mediaTypes->Next(1, &mt, &count) == S_OK) {
|
||||
if (!callback(param, *mt, nullptr))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool EnumPinCaps(IPin *pin, EnumCapsCallback callback, void *param)
|
||||
{
|
||||
HRESULT hr;
|
||||
ComQIPtr<IAMStreamConfig> config(pin);
|
||||
int count, size;
|
||||
|
||||
if (config == NULL)
|
||||
return false;
|
||||
|
||||
hr = config->GetNumberOfCapabilities(&count, &size);
|
||||
if (SUCCEEDED(hr)) {
|
||||
vector<BYTE> caps;
|
||||
caps.resize(size);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
MediaTypePtr mt;
|
||||
hr = config->GetStreamCaps(i, &mt, caps.data());
|
||||
if (SUCCEEDED(hr))
|
||||
if (!callback(param, *mt, caps.data()))
|
||||
break;
|
||||
}
|
||||
} else if (hr == E_NOTIMPL) {
|
||||
EnumElgatoCaps(pin, callback, param);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Note: DEVICE_VideoInfo is not to be confused with Device::VideoInfo */
|
||||
static bool Get_FORMAT_VideoInfo_Data(VideoInfo &info, const AM_MEDIA_TYPE &mt,
|
||||
const BYTE *data)
|
||||
{
|
||||
const VIDEO_STREAM_CONFIG_CAPS *vscc;
|
||||
const VIDEOINFOHEADER *viHeader;
|
||||
const BITMAPINFOHEADER *bmiHeader;
|
||||
VideoFormat format;
|
||||
|
||||
vscc = reinterpret_cast<const VIDEO_STREAM_CONFIG_CAPS *>(data);
|
||||
viHeader = reinterpret_cast<const VIDEOINFOHEADER *>(mt.pbFormat);
|
||||
bmiHeader = &viHeader->bmiHeader;
|
||||
|
||||
if (!GetMediaTypeVFormat(mt, format))
|
||||
return false;
|
||||
|
||||
info.format = format;
|
||||
|
||||
if (vscc) {
|
||||
info.minInterval = vscc->MinFrameInterval;
|
||||
info.maxInterval = vscc->MaxFrameInterval;
|
||||
info.minCX = vscc->MinOutputSize.cx;
|
||||
info.minCY = vscc->MinOutputSize.cy;
|
||||
info.maxCX = vscc->MaxOutputSize.cx;
|
||||
info.maxCY = vscc->MaxOutputSize.cy;
|
||||
|
||||
if (!info.minCX || !info.minCY || !info.maxCX || !info.maxCY) {
|
||||
info.minCX = info.maxCX = bmiHeader->biWidth;
|
||||
info.minCY = info.maxCY = bmiHeader->biHeight;
|
||||
}
|
||||
|
||||
info.granularityCX = max(vscc->OutputGranularityX, 1);
|
||||
info.granularityCY = max(vscc->OutputGranularityY, 1);
|
||||
} else {
|
||||
info.minInterval = info.maxInterval = 10010000000LL / 60000LL;
|
||||
info.minCX = info.maxCX = bmiHeader->biWidth;
|
||||
info.minCY = info.maxCY = bmiHeader->biHeight;
|
||||
info.granularityCX = 1;
|
||||
info.granularityCY = 1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool Get_FORMAT_WaveFormatEx_Data(AudioInfo &info,
|
||||
const AM_MEDIA_TYPE &mt,
|
||||
const BYTE *data)
|
||||
{
|
||||
const AUDIO_STREAM_CONFIG_CAPS *ascc;
|
||||
const WAVEFORMATEX *wfex;
|
||||
|
||||
ascc = reinterpret_cast<const AUDIO_STREAM_CONFIG_CAPS *>(data);
|
||||
wfex = reinterpret_cast<const WAVEFORMATEX *>(mt.pbFormat);
|
||||
if (!wfex || !ascc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (wfex->wBitsPerSample) {
|
||||
case 16:
|
||||
info.format = AudioFormat::Wave16bit;
|
||||
break;
|
||||
case 32:
|
||||
info.format = AudioFormat::WaveFloat;
|
||||
break;
|
||||
}
|
||||
|
||||
info.minChannels = ascc->MinimumChannels;
|
||||
info.maxChannels = ascc->MaximumChannels;
|
||||
info.channelsGranularity = ascc->ChannelsGranularity;
|
||||
info.minSampleRate = ascc->MinimumSampleFrequency;
|
||||
info.maxSampleRate = ascc->MaximumSampleFrequency;
|
||||
info.sampleRateGranularity = ascc->SampleFrequencyGranularity;
|
||||
return true;
|
||||
}
|
||||
|
||||
struct ClosestVideoData {
|
||||
VideoConfig &config;
|
||||
MediaType &mt;
|
||||
long long bestVal;
|
||||
bool found;
|
||||
|
||||
ClosestVideoData &operator=(ClosestVideoData const &) = delete;
|
||||
ClosestVideoData &operator=(ClosestVideoData &&) = delete;
|
||||
|
||||
inline ClosestVideoData(VideoConfig &config, MediaType &mt)
|
||||
: config(config), mt(mt), bestVal(0), found(false)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
static inline void ClampToGranularity(LONG &val, int minVal, int granularity)
|
||||
{
|
||||
val -= ((val - minVal) % granularity);
|
||||
}
|
||||
|
||||
static inline int GetFormatRating(VideoFormat format)
|
||||
{
|
||||
if (format >= VideoFormat::I420 && format < VideoFormat::YVYU)
|
||||
return 0;
|
||||
else if (format >= VideoFormat::YVYU && format < VideoFormat::MJPEG)
|
||||
return 5;
|
||||
else if (format == VideoFormat::MJPEG)
|
||||
return 10;
|
||||
|
||||
return 15;
|
||||
}
|
||||
|
||||
static bool ClosestVideoMTCallback(ClosestVideoData &data,
|
||||
const AM_MEDIA_TYPE &mt, const BYTE *capData)
|
||||
{
|
||||
VideoInfo info;
|
||||
|
||||
if (mt.formattype == FORMAT_VideoInfo) {
|
||||
if (!Get_FORMAT_VideoInfo_Data(info, mt, capData))
|
||||
return true;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
|
||||
MediaType copiedMT = mt;
|
||||
VIDEOINFOHEADER *vih = (VIDEOINFOHEADER *)copiedMT->pbFormat;
|
||||
BITMAPINFOHEADER *bmih = GetBitmapInfoHeader(copiedMT);
|
||||
|
||||
if (data.config.internalFormat != VideoFormat::Any &&
|
||||
data.config.internalFormat != info.format)
|
||||
return true;
|
||||
|
||||
int xVal = 0;
|
||||
int yVal = 0;
|
||||
int formatVal = 0;
|
||||
long long frameVal = 0;
|
||||
|
||||
if (data.config.cx < info.minCX)
|
||||
xVal = info.minCX - data.config.cx;
|
||||
else if (data.config.cx > info.maxCX)
|
||||
xVal = data.config.cx - info.maxCX;
|
||||
|
||||
const int absMinCY = abs(info.minCY);
|
||||
const int absMaxCY = abs(info.maxCY);
|
||||
if (data.config.cy_abs < absMinCY)
|
||||
yVal = absMinCY - data.config.cy_abs;
|
||||
else if (data.config.cy_abs > absMaxCY)
|
||||
yVal = data.config.cy_abs - absMaxCY;
|
||||
|
||||
const long long frameInterval = data.config.frameInterval;
|
||||
if (frameInterval < info.minInterval)
|
||||
frameVal = info.minInterval - frameInterval;
|
||||
else if (frameInterval > info.maxInterval)
|
||||
frameVal = frameInterval - info.maxInterval;
|
||||
|
||||
formatVal = GetFormatRating(info.format);
|
||||
|
||||
long long totalVal = frameVal + yVal + xVal + formatVal;
|
||||
|
||||
if (!data.found || data.bestVal > totalVal) {
|
||||
if (xVal == 0) {
|
||||
bmih->biWidth = data.config.cx;
|
||||
ClampToGranularity(bmih->biWidth, info.minCX,
|
||||
info.granularityCX);
|
||||
}
|
||||
|
||||
if (yVal == 0) {
|
||||
LONG cy_abs_clamp = data.config.cy_abs;
|
||||
ClampToGranularity(cy_abs_clamp, info.minCY,
|
||||
info.granularityCY);
|
||||
bmih->biHeight = data.config.cy_flip ? -cy_abs_clamp
|
||||
: cy_abs_clamp;
|
||||
}
|
||||
|
||||
if (frameVal == 0) {
|
||||
// Close enough. Fixes GV-USB2 29.97 FPS setting.
|
||||
if (abs(vih->AvgTimePerFrame - frameInterval) > 1)
|
||||
vih->AvgTimePerFrame = frameInterval;
|
||||
}
|
||||
|
||||
data.found = true;
|
||||
data.bestVal = totalVal;
|
||||
data.mt = copiedMT;
|
||||
|
||||
if (totalVal == 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetClosestVideoMediaType(IBaseFilter *filter, VideoConfig &config,
|
||||
MediaType &mt)
|
||||
{
|
||||
ComPtr<IPin> pin;
|
||||
ClosestVideoData data(config, mt);
|
||||
bool success;
|
||||
|
||||
success = GetFilterPin(filter, MEDIATYPE_Video, PIN_CATEGORY_CAPTURE,
|
||||
PINDIR_OUTPUT, &pin);
|
||||
if (!success || pin == NULL) {
|
||||
Error(L"GetClosestVideoMediaType: Could not get pin");
|
||||
return false;
|
||||
}
|
||||
|
||||
success = EnumPinCaps(pin, EnumCapsCallback(ClosestVideoMTCallback),
|
||||
&data);
|
||||
if (!success) {
|
||||
Error(L"GetClosestVideoMediaType: Could not enumerate caps");
|
||||
return false;
|
||||
}
|
||||
|
||||
return data.found;
|
||||
}
|
||||
|
||||
struct ClosestAudioData {
|
||||
AudioConfig &config;
|
||||
MediaType &mt;
|
||||
int bestVal;
|
||||
bool found;
|
||||
|
||||
ClosestAudioData &operator=(ClosestAudioData const &) = delete;
|
||||
ClosestAudioData &operator=(ClosestAudioData &&) = delete;
|
||||
|
||||
inline ClosestAudioData(AudioConfig &config, MediaType &mt)
|
||||
: config(config), mt(mt), bestVal(0), found(false)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
static bool ClosestAudioMTCallback(ClosestAudioData &data,
|
||||
const AM_MEDIA_TYPE &mt, const BYTE *capData)
|
||||
{
|
||||
AudioInfo info = {};
|
||||
|
||||
if (mt.formattype == FORMAT_WaveFormatEx) {
|
||||
if (!Get_FORMAT_WaveFormatEx_Data(info, mt, capData))
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
|
||||
MediaType copiedMT = mt;
|
||||
WAVEFORMATEX *wfex = (WAVEFORMATEX *)copiedMT->pbFormat;
|
||||
|
||||
if (data.config.format != AudioFormat::Any &&
|
||||
data.config.format != info.format)
|
||||
return true;
|
||||
|
||||
int sampleRateVal = 0;
|
||||
int channelsVal = 0;
|
||||
|
||||
if (data.config.sampleRate < info.minSampleRate)
|
||||
sampleRateVal = info.minSampleRate - data.config.sampleRate;
|
||||
else if (data.config.sampleRate > info.maxSampleRate)
|
||||
sampleRateVal = data.config.sampleRate - info.maxSampleRate;
|
||||
else if (data.config.sampleRate == info.minSampleRate)
|
||||
sampleRateVal = data.config.sampleRate;
|
||||
else if (data.config.sampleRate == info.maxSampleRate)
|
||||
sampleRateVal = data.config.sampleRate;
|
||||
|
||||
if (data.config.channels < info.minChannels)
|
||||
channelsVal = info.minChannels - data.config.channels;
|
||||
else if (data.config.channels == info.minChannels)
|
||||
channelsVal = info.minChannels;
|
||||
else if (info.maxChannels > data.config.channels)
|
||||
channelsVal = data.config.channels - info.maxChannels;
|
||||
else if (data.config.channels == info.maxChannels)
|
||||
channelsVal = data.config.channels;
|
||||
|
||||
int totalVal = sampleRateVal + channelsVal;
|
||||
|
||||
if (!data.found || data.bestVal > totalVal) {
|
||||
if (channelsVal == 0) {
|
||||
LONG channels = data.config.channels;
|
||||
ClampToGranularity(channels, info.minChannels,
|
||||
info.channelsGranularity);
|
||||
wfex->nChannels = (WORD)channels;
|
||||
|
||||
wfex->nBlockAlign =
|
||||
wfex->wBitsPerSample * wfex->nChannels / 8;
|
||||
}
|
||||
|
||||
if (sampleRateVal == 0) {
|
||||
wfex->nSamplesPerSec = data.config.sampleRate;
|
||||
ClampToGranularity((LONG &)wfex->nSamplesPerSec,
|
||||
info.minSampleRate,
|
||||
info.sampleRateGranularity);
|
||||
}
|
||||
|
||||
wfex->nAvgBytesPerSec =
|
||||
wfex->nSamplesPerSec * wfex->nBlockAlign;
|
||||
|
||||
data.mt = copiedMT;
|
||||
data.found = true;
|
||||
data.bestVal = totalVal;
|
||||
|
||||
if (totalVal == 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetClosestAudioMediaType(IBaseFilter *filter, AudioConfig &config,
|
||||
MediaType &mt)
|
||||
{
|
||||
ComPtr<IPin> pin;
|
||||
ClosestAudioData data(config, mt);
|
||||
bool success;
|
||||
|
||||
success = GetFilterPin(filter, MEDIATYPE_Audio, PIN_CATEGORY_CAPTURE,
|
||||
PINDIR_OUTPUT, &pin);
|
||||
if (!success || pin == NULL) {
|
||||
Error(L"GetClosestAudioMediaType: Could not get pin");
|
||||
return false;
|
||||
}
|
||||
|
||||
success = EnumPinCaps(pin, EnumCapsCallback(ClosestAudioMTCallback),
|
||||
&data);
|
||||
if (!success) {
|
||||
Error(L"GetClosestAudioMediaType: Could not enumerate caps");
|
||||
return false;
|
||||
}
|
||||
|
||||
return data.found;
|
||||
}
|
||||
|
||||
static bool EnumVideoCap(vector<VideoInfo> &caps, const AM_MEDIA_TYPE &mt,
|
||||
const BYTE *data)
|
||||
{
|
||||
VideoInfo info;
|
||||
|
||||
if (mt.formattype == FORMAT_VideoInfo)
|
||||
if (Get_FORMAT_VideoInfo_Data(info, mt, data))
|
||||
caps.push_back(info);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EnumVideoCaps(IPin *pin, vector<VideoInfo> &caps)
|
||||
{
|
||||
return EnumPinCaps(pin, EnumCapsCallback(EnumVideoCap), &caps);
|
||||
}
|
||||
|
||||
static bool EnumAudioCap(vector<AudioInfo> &caps, const AM_MEDIA_TYPE &mt,
|
||||
const BYTE *data)
|
||||
{
|
||||
AudioInfo info;
|
||||
|
||||
if (mt.formattype == FORMAT_WaveFormatEx) {
|
||||
if (Get_FORMAT_WaveFormatEx_Data(info, mt, data))
|
||||
caps.push_back(info);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EnumAudioCaps(IPin *pin, vector<AudioInfo> &caps)
|
||||
{
|
||||
return EnumPinCaps(pin, EnumCapsCallback(EnumAudioCap), &caps);
|
||||
}
|
||||
|
||||
static bool decklinkVideoPresent = false;
|
||||
|
||||
static bool EnumDevice(const GUID &type, IMoniker *deviceInfo,
|
||||
EnumDeviceCallback callback, void *param)
|
||||
{
|
||||
ComPtr<IPropertyBag> propertyData;
|
||||
ComPtr<IBaseFilter> filter;
|
||||
HRESULT hr;
|
||||
|
||||
hr = deviceInfo->BindToStorage(0, 0, IID_IPropertyBag,
|
||||
(void **)&propertyData);
|
||||
if (FAILED(hr))
|
||||
return true;
|
||||
|
||||
VARIANT deviceName, devicePath;
|
||||
deviceName.vt = VT_BSTR;
|
||||
devicePath.vt = VT_BSTR;
|
||||
devicePath.bstrVal = NULL;
|
||||
|
||||
hr = propertyData->Read(L"FriendlyName", &deviceName, NULL);
|
||||
if (FAILED(hr))
|
||||
return true;
|
||||
|
||||
/* workaround to a crash in decklink drivers; if no decklink device
|
||||
* is plugged in to the system, it will still try to enumerate the
|
||||
* decklink audio device, but will crash when trying to bind it to
|
||||
* a filter due to a bug in the drivers */
|
||||
if (deviceName.bstrVal && type == CLSID_AudioInputDeviceCategory &&
|
||||
wcsstr(deviceName.bstrVal, L"Decklink") != nullptr &&
|
||||
!decklinkVideoPresent) {
|
||||
return true;
|
||||
}
|
||||
|
||||
propertyData->Read(L"DevicePath", &devicePath, NULL);
|
||||
|
||||
hr = deviceInfo->BindToObject(NULL, 0, IID_IBaseFilter,
|
||||
(void **)&filter);
|
||||
if (SUCCEEDED(hr)) {
|
||||
if (!callback(param, filter, deviceName.bstrVal,
|
||||
devicePath.bstrVal))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool EnumExceptionVideoDevices(EnumDeviceCallback callback, void *param)
|
||||
{
|
||||
ComPtr<IBaseFilter> filter;
|
||||
HRESULT hr;
|
||||
|
||||
hr = CoCreateInstance(CLSID_ElgatoVideoCaptureFilter, nullptr,
|
||||
CLSCTX_INPROC_SERVER, IID_IBaseFilter,
|
||||
(void **)&filter);
|
||||
if (SUCCEEDED(hr)) {
|
||||
if (!callback(param, filter, L"Elgato Game Capture HD",
|
||||
L"__elgato"))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static recursive_mutex enumMutex;
|
||||
|
||||
static bool CheckForDLCallback(void *unused, IBaseFilter *filter,
|
||||
const wchar_t *deviceName,
|
||||
const wchar_t *devicePath)
|
||||
{
|
||||
if (wcsstr(deviceName, L"Decklink") != nullptr) {
|
||||
decklinkVideoPresent = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
DSHOW_UNUSED(unused);
|
||||
DSHOW_UNUSED(filter);
|
||||
DSHOW_UNUSED(devicePath);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void CheckForDecklinkVideo()
|
||||
{
|
||||
decklinkVideoPresent = false;
|
||||
EnumDevices(CLSID_VideoInputDeviceCategory, CheckForDLCallback,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
bool EnumDevices(const GUID &type, EnumDeviceCallback callback, void *param)
|
||||
{
|
||||
lock_guard<recursive_mutex> lock(enumMutex);
|
||||
ComPtr<ICreateDevEnum> deviceEnum;
|
||||
ComPtr<IEnumMoniker> enumMoniker;
|
||||
ComPtr<IMoniker> deviceInfo;
|
||||
HRESULT hr;
|
||||
DWORD count = 0;
|
||||
|
||||
if (type == CLSID_AudioInputDeviceCategory) {
|
||||
CheckForDecklinkVideo();
|
||||
}
|
||||
|
||||
hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL,
|
||||
CLSCTX_INPROC_SERVER, IID_ICreateDevEnum,
|
||||
(void **)&deviceEnum);
|
||||
if (FAILED(hr)) {
|
||||
WarningHR(L"EnumAudioDevices: Could not create "
|
||||
L"ICreateDeviceEnum",
|
||||
hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
hr = deviceEnum->CreateClassEnumerator(type, &enumMoniker, 0);
|
||||
if (FAILED(hr)) {
|
||||
WarningHR(L"EnumAudioDevices: CreateClassEnumerator failed",
|
||||
hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hr == S_OK) {
|
||||
while (enumMoniker->Next(1, &deviceInfo, &count) == S_OK) {
|
||||
if (!EnumDevice(type, deviceInfo, callback, param))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (type == CLSID_VideoInputDeviceCategory)
|
||||
if (!EnumExceptionVideoDevices(callback, param))
|
||||
return true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}; /* namespace DShow */
|
||||
46
obs-studio/deps/libdshowcapture/src/source/dshow-enum.hpp
Normal file
46
obs-studio/deps/libdshowcapture/src/source/dshow-enum.hpp
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Lain Bailey <lain@obsproject.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../dshowcapture.hpp"
|
||||
#include "dshow-base.hpp"
|
||||
#include "dshow-media-type.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace DShow {
|
||||
|
||||
bool GetClosestVideoMediaType(IBaseFilter *filter, VideoConfig &config,
|
||||
MediaType &mt);
|
||||
bool GetClosestAudioMediaType(IBaseFilter *filter, AudioConfig &config,
|
||||
MediaType &mt);
|
||||
|
||||
bool EnumVideoCaps(IPin *pin, vector<VideoInfo> &caps);
|
||||
bool EnumAudioCaps(IPin *pin, vector<AudioInfo> &caps);
|
||||
|
||||
typedef bool (*EnumDeviceCallback)(void *param, IBaseFilter *filter,
|
||||
const wchar_t *deviceName,
|
||||
const wchar_t *devicePath);
|
||||
|
||||
bool EnumDevices(const GUID &type, EnumDeviceCallback callback, void *param);
|
||||
|
||||
}; /* namespace DShow */
|
||||
335
obs-studio/deps/libdshowcapture/src/source/dshow-formats.cpp
Normal file
335
obs-studio/deps/libdshowcapture/src/source/dshow-formats.cpp
Normal file
@ -0,0 +1,335 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Lain Bailey <lain@obsproject.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
#include "dshow-formats.hpp"
|
||||
#include "dshow-media-type.hpp"
|
||||
|
||||
#ifndef __MINGW32__
|
||||
|
||||
const GUID MEDIASUBTYPE_RAW_AAC1 = {0x000000FF,
|
||||
0x0000,
|
||||
0x0010,
|
||||
{0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b,
|
||||
0x71}};
|
||||
|
||||
const GUID MEDIASUBTYPE_I420 = {0x30323449,
|
||||
0x0000,
|
||||
0x0010,
|
||||
{0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b,
|
||||
0x71}};
|
||||
|
||||
const GUID MEDIASUBTYPE_DVM = {0x00002000,
|
||||
0x0000,
|
||||
0x0010,
|
||||
{0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b,
|
||||
0x71}};
|
||||
|
||||
#endif
|
||||
|
||||
const GUID MEDIASUBTYPE_Y800 = {0x30303859,
|
||||
0x0000,
|
||||
0x0010,
|
||||
{0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b,
|
||||
0x71}};
|
||||
|
||||
#ifdef ENABLE_HEVC
|
||||
const GUID MEDIASUBTYPE_HEVC = {0x43564548,
|
||||
0x0000,
|
||||
0x0010,
|
||||
{0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b,
|
||||
0x71}};
|
||||
#endif
|
||||
|
||||
namespace DShow {
|
||||
|
||||
DWORD VFormatToFourCC(VideoFormat format)
|
||||
{
|
||||
switch (format) {
|
||||
/* raw formats */
|
||||
case VideoFormat::ARGB:
|
||||
return MAKEFOURCC('A', 'R', 'G', 'B');
|
||||
case VideoFormat::XRGB:
|
||||
return MAKEFOURCC('R', 'G', 'B', '4');
|
||||
|
||||
/* planar YUV formats */
|
||||
case VideoFormat::I420:
|
||||
return MAKEFOURCC('I', '4', '2', '0');
|
||||
case VideoFormat::NV12:
|
||||
return MAKEFOURCC('N', 'V', '1', '2');
|
||||
case VideoFormat::YV12:
|
||||
return MAKEFOURCC('Y', 'V', '1', '2');
|
||||
case VideoFormat::Y800:
|
||||
return MAKEFOURCC('Y', '8', '0', '0');
|
||||
case VideoFormat::P010:
|
||||
return MAKEFOURCC('P', '0', '1', '0');
|
||||
|
||||
/* packed YUV formats */
|
||||
case VideoFormat::YVYU:
|
||||
return MAKEFOURCC('Y', 'V', 'Y', 'U');
|
||||
case VideoFormat::YUY2:
|
||||
return MAKEFOURCC('Y', 'U', 'Y', '2');
|
||||
case VideoFormat::UYVY:
|
||||
return MAKEFOURCC('U', 'Y', 'V', 'Y');
|
||||
case VideoFormat::HDYC:
|
||||
return MAKEFOURCC('H', 'D', 'Y', 'C');
|
||||
|
||||
/* encoded formats */
|
||||
case VideoFormat::MJPEG:
|
||||
return MAKEFOURCC('M', 'J', 'P', 'G');
|
||||
case VideoFormat::H264:
|
||||
return MAKEFOURCC('H', '2', '6', '4');
|
||||
#ifdef ENABLE_HEVC
|
||||
case VideoFormat::HEVC:
|
||||
return MAKEFOURCC('H', 'E', 'V', 'C');
|
||||
#endif
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
GUID VFormatToSubType(VideoFormat format)
|
||||
{
|
||||
switch (format) {
|
||||
/* raw formats */
|
||||
case VideoFormat::ARGB:
|
||||
return MEDIASUBTYPE_ARGB32;
|
||||
case VideoFormat::XRGB:
|
||||
return MEDIASUBTYPE_RGB32;
|
||||
|
||||
/* planar YUV formats */
|
||||
case VideoFormat::I420:
|
||||
return MEDIASUBTYPE_I420;
|
||||
case VideoFormat::NV12:
|
||||
return MEDIASUBTYPE_NV12;
|
||||
case VideoFormat::YV12:
|
||||
return MEDIASUBTYPE_YV12;
|
||||
case VideoFormat::Y800:
|
||||
return MEDIASUBTYPE_Y800;
|
||||
case VideoFormat::P010:
|
||||
return MEDIASUBTYPE_P010;
|
||||
|
||||
/* packed YUV formats */
|
||||
case VideoFormat::YVYU:
|
||||
return MEDIASUBTYPE_YVYU;
|
||||
case VideoFormat::YUY2:
|
||||
return MEDIASUBTYPE_YUY2;
|
||||
case VideoFormat::UYVY:
|
||||
return MEDIASUBTYPE_UYVY;
|
||||
|
||||
/* encoded formats */
|
||||
case VideoFormat::MJPEG:
|
||||
return MEDIASUBTYPE_MJPG;
|
||||
case VideoFormat::H264:
|
||||
return MEDIASUBTYPE_H264;
|
||||
#ifdef ENABLE_HEVC
|
||||
case VideoFormat::HEVC:
|
||||
return MEDIASUBTYPE_HEVC;
|
||||
#endif
|
||||
|
||||
default:
|
||||
return GUID();
|
||||
}
|
||||
}
|
||||
|
||||
WORD VFormatBits(VideoFormat format)
|
||||
{
|
||||
switch (format) {
|
||||
/* raw formats */
|
||||
case VideoFormat::ARGB:
|
||||
case VideoFormat::XRGB:
|
||||
return 32;
|
||||
|
||||
/* planar YUV formats */
|
||||
case VideoFormat::I420:
|
||||
case VideoFormat::NV12:
|
||||
case VideoFormat::YV12:
|
||||
return 12;
|
||||
case VideoFormat::Y800:
|
||||
return 8;
|
||||
|
||||
/* packed YUV formats */
|
||||
case VideoFormat::YVYU:
|
||||
case VideoFormat::YUY2:
|
||||
case VideoFormat::UYVY:
|
||||
return 16;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
WORD VFormatPlanes(VideoFormat format)
|
||||
{
|
||||
switch (format) {
|
||||
/* raw formats */
|
||||
case VideoFormat::ARGB:
|
||||
case VideoFormat::XRGB:
|
||||
return 1;
|
||||
|
||||
/* planar YUV formats */
|
||||
case VideoFormat::I420:
|
||||
return 3;
|
||||
case VideoFormat::NV12:
|
||||
case VideoFormat::YV12:
|
||||
return 2;
|
||||
case VideoFormat::Y800:
|
||||
return 1;
|
||||
|
||||
/* packed YUV formats */
|
||||
case VideoFormat::YVYU:
|
||||
case VideoFormat::YUY2:
|
||||
case VideoFormat::UYVY:
|
||||
return 1;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static bool GetFourCCVFormat(DWORD fourCC, VideoFormat &format)
|
||||
{
|
||||
switch (fourCC) {
|
||||
/* raw formats */
|
||||
case MAKEFOURCC('R', 'G', 'B', '2'):
|
||||
format = VideoFormat::XRGB;
|
||||
break;
|
||||
case MAKEFOURCC('R', 'G', 'B', '4'):
|
||||
format = VideoFormat::XRGB;
|
||||
break;
|
||||
case MAKEFOURCC('A', 'R', 'G', 'B'):
|
||||
format = VideoFormat::ARGB;
|
||||
break;
|
||||
|
||||
/* planar YUV formats */
|
||||
case MAKEFOURCC('I', '4', '2', '0'):
|
||||
case MAKEFOURCC('I', 'Y', 'U', 'V'):
|
||||
format = VideoFormat::I420;
|
||||
break;
|
||||
case MAKEFOURCC('Y', 'V', '1', '2'):
|
||||
format = VideoFormat::YV12;
|
||||
break;
|
||||
case MAKEFOURCC('N', 'V', '1', '2'):
|
||||
format = VideoFormat::NV12;
|
||||
break;
|
||||
case MAKEFOURCC('Y', '8', '0', '0'):
|
||||
format = VideoFormat::Y800;
|
||||
break;
|
||||
case MAKEFOURCC('P', '0', '1', '0'):
|
||||
format = VideoFormat::P010;
|
||||
break;
|
||||
|
||||
/* packed YUV formats */
|
||||
case MAKEFOURCC('Y', 'V', 'Y', 'U'):
|
||||
format = VideoFormat::YVYU;
|
||||
break;
|
||||
case MAKEFOURCC('Y', 'U', 'Y', '2'):
|
||||
format = VideoFormat::YUY2;
|
||||
break;
|
||||
case MAKEFOURCC('U', 'Y', 'V', 'Y'):
|
||||
format = VideoFormat::UYVY;
|
||||
break;
|
||||
case MAKEFOURCC('H', 'D', 'Y', 'C'):
|
||||
format = VideoFormat::HDYC;
|
||||
break;
|
||||
|
||||
/* compressed formats */
|
||||
case MAKEFOURCC('H', '2', '6', '4'):
|
||||
format = VideoFormat::H264;
|
||||
break;
|
||||
#ifdef ENABLE_HEVC
|
||||
case MAKEFOURCC('H', 'E', 'V', 'C'):
|
||||
format = VideoFormat::HEVC;
|
||||
break;
|
||||
#endif
|
||||
|
||||
/* compressed formats that can automatically create intermediary
|
||||
* filters for decompression */
|
||||
case MAKEFOURCC('M', 'J', 'P', 'G'):
|
||||
format = VideoFormat::MJPEG;
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetMediaTypeVFormat(const AM_MEDIA_TYPE &mt, VideoFormat &format)
|
||||
{
|
||||
if (mt.majortype != MEDIATYPE_Video)
|
||||
return false;
|
||||
|
||||
const BITMAPINFOHEADER *bmih = GetBitmapInfoHeader(mt);
|
||||
|
||||
format = VideoFormat::Unknown;
|
||||
|
||||
/* raw formats */
|
||||
if (mt.subtype == MEDIASUBTYPE_RGB24)
|
||||
format = VideoFormat::XRGB;
|
||||
else if (mt.subtype == MEDIASUBTYPE_RGB32)
|
||||
format = VideoFormat::XRGB;
|
||||
else if (mt.subtype == MEDIASUBTYPE_ARGB32)
|
||||
format = VideoFormat::ARGB;
|
||||
|
||||
/* planar YUV formats */
|
||||
else if (mt.subtype == MEDIASUBTYPE_I420)
|
||||
format = VideoFormat::I420;
|
||||
else if (mt.subtype == MEDIASUBTYPE_IYUV)
|
||||
format = VideoFormat::I420;
|
||||
else if (mt.subtype == MEDIASUBTYPE_YV12)
|
||||
format = VideoFormat::YV12;
|
||||
else if (mt.subtype == MEDIASUBTYPE_NV12)
|
||||
format = VideoFormat::NV12;
|
||||
else if (mt.subtype == MEDIASUBTYPE_Y800)
|
||||
format = VideoFormat::Y800;
|
||||
else if (mt.subtype == MEDIASUBTYPE_P010)
|
||||
format = VideoFormat::P010;
|
||||
|
||||
/* packed YUV formats */
|
||||
else if (mt.subtype == MEDIASUBTYPE_YVYU)
|
||||
format = VideoFormat::YVYU;
|
||||
else if (mt.subtype == MEDIASUBTYPE_YUY2)
|
||||
format = VideoFormat::YUY2;
|
||||
else if (mt.subtype == MEDIASUBTYPE_UYVY)
|
||||
format = VideoFormat::UYVY;
|
||||
|
||||
/* compressed formats */
|
||||
else if (mt.subtype == MEDIASUBTYPE_H264)
|
||||
format = VideoFormat::H264;
|
||||
#ifdef ENABLE_HEVC
|
||||
else if (mt.subtype == MEDIASUBTYPE_HEVC)
|
||||
format = VideoFormat::HEVC;
|
||||
#endif
|
||||
|
||||
/* compressed formats that can automatically create intermediary
|
||||
* filters for decompression */
|
||||
else if (mt.subtype == MEDIASUBTYPE_MJPG)
|
||||
format = VideoFormat::MJPEG;
|
||||
|
||||
/* no valid types, check fourcc value instead */
|
||||
else
|
||||
return bmih ? GetFourCCVFormat(bmih->biCompression, format)
|
||||
: false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}; /* namespace DShow */
|
||||
37
obs-studio/deps/libdshowcapture/src/source/dshow-formats.hpp
Normal file
37
obs-studio/deps/libdshowcapture/src/source/dshow-formats.hpp
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Lain Bailey <lain@obsproject.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../dshowcapture.hpp"
|
||||
#include "dshow-base.hpp"
|
||||
|
||||
#include <wmcodecdsp.h>
|
||||
#include <mmreg.h>
|
||||
|
||||
namespace DShow {
|
||||
|
||||
DWORD VFormatToFourCC(VideoFormat format);
|
||||
WORD VFormatBits(VideoFormat format);
|
||||
WORD VFormatPlanes(VideoFormat format);
|
||||
GUID VFormatToSubType(VideoFormat format);
|
||||
|
||||
bool GetMediaTypeVFormat(const AM_MEDIA_TYPE &mt, VideoFormat &format);
|
||||
|
||||
}; /*namespace DShow */
|
||||
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Lain Bailey <lain@obsproject.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
#include "dshow-media-type.hpp"
|
||||
|
||||
namespace DShow {
|
||||
|
||||
HRESULT CopyMediaType(AM_MEDIA_TYPE *pmtTarget, const AM_MEDIA_TYPE *pmtSource)
|
||||
{
|
||||
if (!pmtSource || !pmtTarget)
|
||||
return S_FALSE;
|
||||
|
||||
*pmtTarget = *pmtSource;
|
||||
|
||||
if (pmtSource->cbFormat && pmtSource->pbFormat) {
|
||||
pmtTarget->pbFormat =
|
||||
(PBYTE)CoTaskMemAlloc(pmtSource->cbFormat);
|
||||
|
||||
if (pmtTarget->pbFormat == nullptr) {
|
||||
pmtTarget->cbFormat = 0;
|
||||
return E_OUTOFMEMORY;
|
||||
} else {
|
||||
memcpy(pmtTarget->pbFormat, pmtSource->pbFormat,
|
||||
pmtTarget->cbFormat);
|
||||
}
|
||||
}
|
||||
|
||||
if (pmtTarget->pUnk != nullptr)
|
||||
pmtTarget->pUnk->AddRef();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void FreeMediaType(AM_MEDIA_TYPE &mt)
|
||||
{
|
||||
if (mt.cbFormat != 0) {
|
||||
CoTaskMemFree((LPVOID)mt.pbFormat);
|
||||
mt.cbFormat = 0;
|
||||
mt.pbFormat = nullptr;
|
||||
}
|
||||
|
||||
if (mt.pUnk) {
|
||||
mt.pUnk->Release();
|
||||
mt.pUnk = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
BITMAPINFOHEADER *GetBitmapInfoHeader(AM_MEDIA_TYPE &mt)
|
||||
{
|
||||
if (mt.formattype == FORMAT_VideoInfo) {
|
||||
VIDEOINFOHEADER *vih;
|
||||
vih = reinterpret_cast<VIDEOINFOHEADER *>(mt.pbFormat);
|
||||
return &vih->bmiHeader;
|
||||
|
||||
} else if (mt.formattype == FORMAT_VideoInfo2) {
|
||||
VIDEOINFOHEADER2 *vih;
|
||||
vih = reinterpret_cast<VIDEOINFOHEADER2 *>(mt.pbFormat);
|
||||
return &vih->bmiHeader;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const BITMAPINFOHEADER *GetBitmapInfoHeader(const AM_MEDIA_TYPE &mt)
|
||||
{
|
||||
if (mt.formattype == FORMAT_VideoInfo) {
|
||||
const VIDEOINFOHEADER *vih;
|
||||
vih = reinterpret_cast<const VIDEOINFOHEADER *>(mt.pbFormat);
|
||||
return &vih->bmiHeader;
|
||||
|
||||
} else if (mt.formattype == FORMAT_VideoInfo2) {
|
||||
const VIDEOINFOHEADER2 *vih;
|
||||
vih = reinterpret_cast<const VIDEOINFOHEADER2 *>(mt.pbFormat);
|
||||
return &vih->bmiHeader;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
}; /* namespace DShow */
|
||||
154
obs-studio/deps/libdshowcapture/src/source/dshow-media-type.hpp
Normal file
154
obs-studio/deps/libdshowcapture/src/source/dshow-media-type.hpp
Normal file
@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Lain Bailey <lain@obsproject.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "dshow-base.hpp"
|
||||
|
||||
namespace DShow {
|
||||
|
||||
HRESULT CopyMediaType(AM_MEDIA_TYPE *pmtTarget, const AM_MEDIA_TYPE *pmtSource);
|
||||
void FreeMediaType(AM_MEDIA_TYPE &mt);
|
||||
|
||||
BITMAPINFOHEADER *GetBitmapInfoHeader(AM_MEDIA_TYPE &mt);
|
||||
const BITMAPINFOHEADER *GetBitmapInfoHeader(const AM_MEDIA_TYPE &mt);
|
||||
|
||||
class MediaTypePtr;
|
||||
|
||||
class MediaType {
|
||||
friend class MediaTypePtr;
|
||||
|
||||
AM_MEDIA_TYPE type;
|
||||
|
||||
public:
|
||||
inline MediaType() { memset(&type, 0, sizeof(type)); }
|
||||
|
||||
inline MediaType(const MediaType &mt)
|
||||
{
|
||||
CopyMediaType(&type, &mt.type);
|
||||
}
|
||||
|
||||
inline MediaType(const AM_MEDIA_TYPE &type_)
|
||||
{
|
||||
CopyMediaType(&type, &type_);
|
||||
}
|
||||
|
||||
inline ~MediaType() { FreeMediaType(type); }
|
||||
|
||||
inline operator AM_MEDIA_TYPE *() { return &type; }
|
||||
inline operator AM_MEDIA_TYPE &() { return type; }
|
||||
inline operator const AM_MEDIA_TYPE *() const { return &type; }
|
||||
inline operator const AM_MEDIA_TYPE &() const { return type; }
|
||||
inline AM_MEDIA_TYPE *Ptr() { return &type; }
|
||||
|
||||
inline AM_MEDIA_TYPE *operator->() { return &type; }
|
||||
inline const AM_MEDIA_TYPE *operator->() const { return &type; }
|
||||
|
||||
inline AM_MEDIA_TYPE *Duplicate() const
|
||||
{
|
||||
AM_MEDIA_TYPE *ptr =
|
||||
(AM_MEDIA_TYPE *)CoTaskMemAlloc(sizeof(*ptr));
|
||||
if (ptr) {
|
||||
memset(ptr, 0, sizeof(*ptr));
|
||||
CopyMediaType(ptr, &type);
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
inline bool operator==(const AM_MEDIA_TYPE *pMT) const
|
||||
{
|
||||
return pMT == &type;
|
||||
}
|
||||
|
||||
inline void operator=(const MediaType &mt)
|
||||
{
|
||||
FreeMediaType(type);
|
||||
CopyMediaType(&type, &mt.type);
|
||||
}
|
||||
|
||||
inline void operator=(const AM_MEDIA_TYPE *pMT)
|
||||
{
|
||||
FreeMediaType(type);
|
||||
CopyMediaType(&type, pMT);
|
||||
}
|
||||
|
||||
inline void operator=(const AM_MEDIA_TYPE &type_)
|
||||
{
|
||||
FreeMediaType(type);
|
||||
CopyMediaType(&type, &type_);
|
||||
}
|
||||
|
||||
template<typename T> inline T *AllocFormat()
|
||||
{
|
||||
if (type.pbFormat) {
|
||||
CoTaskMemFree(type.pbFormat);
|
||||
type.pbFormat = nullptr;
|
||||
type.cbFormat = 0;
|
||||
}
|
||||
|
||||
type.pbFormat = (PBYTE)CoTaskMemAlloc(sizeof(T));
|
||||
type.cbFormat = sizeof(T);
|
||||
memset(type.pbFormat, 0, sizeof(T));
|
||||
|
||||
return (T *)type.pbFormat;
|
||||
}
|
||||
};
|
||||
|
||||
class MediaTypePtr {
|
||||
friend class MediaType;
|
||||
|
||||
AM_MEDIA_TYPE *ptr;
|
||||
|
||||
public:
|
||||
inline void Clear()
|
||||
{
|
||||
if (ptr) {
|
||||
FreeMediaType(*ptr);
|
||||
CoTaskMemFree(ptr);
|
||||
ptr = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
inline MediaTypePtr() : ptr(nullptr) {}
|
||||
inline MediaTypePtr(AM_MEDIA_TYPE *ptr_) : ptr(ptr_) {}
|
||||
inline ~MediaTypePtr() { Clear(); }
|
||||
|
||||
inline AM_MEDIA_TYPE **operator&()
|
||||
{
|
||||
Clear();
|
||||
return &ptr;
|
||||
}
|
||||
|
||||
inline AM_MEDIA_TYPE *operator->() const { return ptr; }
|
||||
|
||||
inline operator AM_MEDIA_TYPE *() const { return ptr; }
|
||||
|
||||
inline void operator=(AM_MEDIA_TYPE *ptr_)
|
||||
{
|
||||
Clear();
|
||||
ptr = ptr_;
|
||||
}
|
||||
|
||||
inline bool operator==(const AM_MEDIA_TYPE *ptr_) const
|
||||
{
|
||||
return ptr == ptr_;
|
||||
}
|
||||
};
|
||||
|
||||
}; /* namespace DShow */
|
||||
313
obs-studio/deps/libdshowcapture/src/source/dshowcapture.cpp
Normal file
313
obs-studio/deps/libdshowcapture/src/source/dshowcapture.cpp
Normal file
@ -0,0 +1,313 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Lain Bailey <lain@obsproject.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
#include "../dshowcapture.hpp"
|
||||
#include "dshow-base.hpp"
|
||||
#include "dshow-enum.hpp"
|
||||
#include "device.hpp"
|
||||
#include "dshow-device-defs.hpp"
|
||||
#include "log.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace DShow {
|
||||
|
||||
Device::Device(InitGraph initialize) : context(new HDevice)
|
||||
{
|
||||
if (initialize == InitGraph::True)
|
||||
context->CreateGraph();
|
||||
}
|
||||
|
||||
Device::~Device()
|
||||
{
|
||||
delete context;
|
||||
}
|
||||
|
||||
bool Device::Valid() const
|
||||
{
|
||||
return context->initialized;
|
||||
}
|
||||
|
||||
bool Device::ResetGraph()
|
||||
{
|
||||
/* cheap and easy way to clear all the filters */
|
||||
delete context;
|
||||
context = new HDevice;
|
||||
|
||||
return context->CreateGraph();
|
||||
}
|
||||
|
||||
void Device::ShutdownGraph()
|
||||
{
|
||||
delete context;
|
||||
context = new HDevice;
|
||||
}
|
||||
|
||||
bool Device::SetVideoConfig(VideoConfig *config)
|
||||
{
|
||||
return context->SetVideoConfig(config);
|
||||
}
|
||||
|
||||
bool Device::SetAudioConfig(AudioConfig *config)
|
||||
{
|
||||
return context->SetAudioConfig(config);
|
||||
}
|
||||
|
||||
bool Device::ConnectFilters()
|
||||
{
|
||||
return context->ConnectFilters();
|
||||
}
|
||||
|
||||
Result Device::Start()
|
||||
{
|
||||
return context->Start();
|
||||
}
|
||||
|
||||
void Device::Stop()
|
||||
{
|
||||
context->Stop();
|
||||
}
|
||||
|
||||
bool Device::GetVideoConfig(VideoConfig &config) const
|
||||
{
|
||||
if (context->videoCapture == NULL)
|
||||
return false;
|
||||
|
||||
config = context->videoConfig;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Device::GetAudioConfig(AudioConfig &config) const
|
||||
{
|
||||
if (context->audioCapture == NULL)
|
||||
return false;
|
||||
|
||||
config = context->audioConfig;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Device::GetVideoDeviceId(DeviceId &id) const
|
||||
{
|
||||
if (context->videoCapture == NULL)
|
||||
return false;
|
||||
|
||||
id = context->videoConfig;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Device::GetAudioDeviceId(DeviceId &id) const
|
||||
{
|
||||
if (context->audioCapture == NULL)
|
||||
return false;
|
||||
|
||||
id = context->audioConfig;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void OpenPropertyPages(HWND hwnd, IUnknown *propertyObject)
|
||||
{
|
||||
if (!propertyObject)
|
||||
return;
|
||||
|
||||
ComQIPtr<ISpecifyPropertyPages> pages(propertyObject);
|
||||
CAUUID cauuid;
|
||||
|
||||
if (pages != NULL) {
|
||||
if (SUCCEEDED(pages->GetPages(&cauuid)) && cauuid.cElems) {
|
||||
OleCreatePropertyFrame(hwnd, 0, 0, NULL, 1,
|
||||
(LPUNKNOWN *)&propertyObject,
|
||||
cauuid.cElems, cauuid.pElems, 0,
|
||||
0, NULL);
|
||||
CoTaskMemFree(cauuid.pElems);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Device::OpenDialog(void *hwnd, DialogType type) const
|
||||
{
|
||||
ComPtr<IUnknown> ptr;
|
||||
HRESULT hr;
|
||||
|
||||
if (type == DialogType::ConfigVideo) {
|
||||
ptr = context->videoFilter;
|
||||
} else if (type == DialogType::ConfigCrossbar ||
|
||||
type == DialogType::ConfigCrossbar2) {
|
||||
hr = context->builder->FindInterface(NULL, NULL,
|
||||
context->videoFilter,
|
||||
IID_IAMCrossbar,
|
||||
(void **)&ptr);
|
||||
if (FAILED(hr)) {
|
||||
WarningHR(L"Failed to find crossbar", hr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ptr != NULL && type == DialogType::ConfigCrossbar2) {
|
||||
ComQIPtr<IAMCrossbar> xbar(ptr);
|
||||
ComQIPtr<IBaseFilter> filter(xbar);
|
||||
|
||||
hr = context->builder->FindInterface(
|
||||
&LOOK_UPSTREAM_ONLY, NULL, filter,
|
||||
IID_IAMCrossbar, (void **)&ptr);
|
||||
if (FAILED(hr)) {
|
||||
WarningHR(L"Failed to find crossbar2", hr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (type == DialogType::ConfigAudio) {
|
||||
ptr = context->audioFilter;
|
||||
}
|
||||
|
||||
if (!ptr) {
|
||||
Warning(L"Could not find filter to open dialog type: %d with",
|
||||
(int)type);
|
||||
return;
|
||||
}
|
||||
|
||||
OpenPropertyPages((HWND)hwnd, ptr);
|
||||
}
|
||||
|
||||
static void EnumEncodedVideo(std::vector<VideoDevice> &devices,
|
||||
const wchar_t *deviceName,
|
||||
const wchar_t *devicePath,
|
||||
const EncodedDevice &info)
|
||||
{
|
||||
VideoDevice device;
|
||||
VideoInfo caps;
|
||||
|
||||
device.name = deviceName;
|
||||
device.path = devicePath;
|
||||
device.audioAttached = true;
|
||||
device.separateAudioFilter = false;
|
||||
|
||||
caps.minCX = caps.maxCX = info.width;
|
||||
caps.minCY = caps.maxCY = info.height;
|
||||
caps.granularityCX = caps.granularityCY = 1;
|
||||
caps.minInterval = caps.maxInterval = info.frameInterval;
|
||||
caps.format = info.videoFormat;
|
||||
|
||||
device.caps.push_back(caps);
|
||||
devices.push_back(device);
|
||||
}
|
||||
|
||||
static void EnumExceptionVideoDevice(std::vector<VideoDevice> &devices,
|
||||
IBaseFilter *filter,
|
||||
const wchar_t *deviceName,
|
||||
const wchar_t *devicePath)
|
||||
{
|
||||
ComPtr<IPin> pin;
|
||||
|
||||
if (GetPinByName(filter, PINDIR_OUTPUT, L"656", &pin))
|
||||
EnumEncodedVideo(devices, deviceName, devicePath, HD_PVR2);
|
||||
|
||||
else if (GetPinByName(filter, PINDIR_OUTPUT, L"TS Out", &pin))
|
||||
EnumEncodedVideo(devices, deviceName, devicePath, Roxio);
|
||||
}
|
||||
|
||||
static bool EnumVideoDevice(std::vector<VideoDevice> &devices,
|
||||
IBaseFilter *filter, const wchar_t *deviceName,
|
||||
const wchar_t *devicePath)
|
||||
{
|
||||
ComPtr<IPin> pin;
|
||||
ComPtr<IPin> audioPin;
|
||||
ComPtr<IBaseFilter> audioFilter;
|
||||
VideoDevice info;
|
||||
|
||||
if (wcsstr(deviceName, L"C875") != nullptr ||
|
||||
wcsstr(deviceName, L"Prif Streambox") != nullptr ||
|
||||
wcsstr(deviceName, L"C835") != nullptr) {
|
||||
EnumEncodedVideo(devices, deviceName, devicePath, AV_LGP);
|
||||
return true;
|
||||
|
||||
} else if (wcsstr(deviceName, L"Hauppauge HD PVR Capture") != nullptr) {
|
||||
EnumEncodedVideo(devices, deviceName, devicePath, HD_PVR1);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool success = GetFilterPin(filter, MEDIATYPE_Video,
|
||||
PIN_CATEGORY_CAPTURE, PINDIR_OUTPUT, &pin);
|
||||
|
||||
/* if this device has no standard capture pin, see if it's an
|
||||
* encoded device, and get its information if so (all encoded devices
|
||||
* are exception devices pretty much) */
|
||||
if (!success) {
|
||||
EnumExceptionVideoDevice(devices, filter, deviceName,
|
||||
devicePath);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!EnumVideoCaps(pin, info.caps))
|
||||
return true;
|
||||
|
||||
info.audioAttached = GetFilterPin(filter, MEDIATYPE_Audio,
|
||||
PIN_CATEGORY_CAPTURE, PINDIR_OUTPUT,
|
||||
&audioPin);
|
||||
|
||||
// Fallback: Find a corresponding audio filter for the same device
|
||||
if (!info.audioAttached) {
|
||||
info.separateAudioFilter =
|
||||
GetDeviceAudioFilter(devicePath, &audioFilter);
|
||||
info.audioAttached = info.separateAudioFilter;
|
||||
}
|
||||
|
||||
info.name = deviceName;
|
||||
if (devicePath)
|
||||
info.path = devicePath;
|
||||
|
||||
devices.push_back(info);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Device::EnumVideoDevices(std::vector<VideoDevice> &devices)
|
||||
{
|
||||
devices.clear();
|
||||
return EnumDevices(CLSID_VideoInputDeviceCategory,
|
||||
EnumDeviceCallback(EnumVideoDevice), &devices);
|
||||
}
|
||||
|
||||
static bool EnumAudioDevice(vector<AudioDevice> &devices, IBaseFilter *filter,
|
||||
const wchar_t *deviceName,
|
||||
const wchar_t *devicePath)
|
||||
{
|
||||
ComPtr<IPin> pin;
|
||||
AudioDevice info;
|
||||
|
||||
bool success = GetFilterPin(filter, MEDIATYPE_Audio,
|
||||
PIN_CATEGORY_CAPTURE, PINDIR_OUTPUT, &pin);
|
||||
if (!success)
|
||||
return true;
|
||||
|
||||
if (!EnumAudioCaps(pin, info.caps))
|
||||
return true;
|
||||
|
||||
info.name = deviceName;
|
||||
if (devicePath)
|
||||
info.path = devicePath;
|
||||
|
||||
devices.push_back(info);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Device::EnumAudioDevices(vector<AudioDevice> &devices)
|
||||
{
|
||||
devices.clear();
|
||||
return EnumDevices(CLSID_AudioInputDeviceCategory,
|
||||
EnumDeviceCallback(EnumAudioDevice), &devices);
|
||||
}
|
||||
|
||||
}; /* namespace DShow */
|
||||
110
obs-studio/deps/libdshowcapture/src/source/dshowencode.cpp
Normal file
110
obs-studio/deps/libdshowcapture/src/source/dshowencode.cpp
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Lain Bailey <lain@obsproject.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
#include "../dshowcapture.hpp"
|
||||
#include "dshow-base.hpp"
|
||||
#include "dshow-enum.hpp"
|
||||
#include "encoder.hpp"
|
||||
#include "log.hpp"
|
||||
|
||||
namespace DShow {
|
||||
|
||||
VideoEncoder::VideoEncoder() : context(new HVideoEncoder) {}
|
||||
|
||||
VideoEncoder::~VideoEncoder()
|
||||
{
|
||||
delete context;
|
||||
}
|
||||
|
||||
bool VideoEncoder::Valid() const
|
||||
{
|
||||
return context->initialized;
|
||||
}
|
||||
|
||||
bool VideoEncoder::Active() const
|
||||
{
|
||||
return context->active;
|
||||
}
|
||||
|
||||
bool VideoEncoder::ResetGraph()
|
||||
{
|
||||
delete context;
|
||||
context = new HVideoEncoder;
|
||||
|
||||
return context->initialized;
|
||||
}
|
||||
|
||||
bool VideoEncoder::SetConfig(VideoEncoderConfig &config)
|
||||
{
|
||||
if (context->active) {
|
||||
delete context;
|
||||
context = new HVideoEncoder;
|
||||
}
|
||||
|
||||
return context->SetConfig(config);
|
||||
}
|
||||
|
||||
bool VideoEncoder::GetConfig(VideoEncoderConfig &config) const
|
||||
{
|
||||
if (context->encoder == nullptr)
|
||||
return false;
|
||||
|
||||
config = context->config;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VideoEncoder::Encode(unsigned char *data[DSHOW_MAX_PLANES],
|
||||
size_t linesize[DSHOW_MAX_PLANES],
|
||||
long long timestampStart, long long timestampEnd,
|
||||
EncoderPacket &packet, bool &new_packet)
|
||||
{
|
||||
if (context->encoder == nullptr)
|
||||
return false;
|
||||
|
||||
return context->Encode(data, linesize, timestampStart, timestampEnd,
|
||||
packet, new_packet);
|
||||
}
|
||||
|
||||
static bool EnumVideoEncoder(vector<DeviceId> &encoders, IBaseFilter *encoder,
|
||||
const wchar_t *deviceName,
|
||||
const wchar_t *devicePath)
|
||||
{
|
||||
DeviceId id;
|
||||
bool validDevice = wcsstr(deviceName, L"C985") ||
|
||||
wcsstr(deviceName, L"C353");
|
||||
|
||||
if (!validDevice)
|
||||
return true;
|
||||
|
||||
id.name = deviceName;
|
||||
id.path = devicePath;
|
||||
encoders.push_back(id);
|
||||
|
||||
(void)encoder;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VideoEncoder::EnumEncoders(vector<DeviceId> &encoders)
|
||||
{
|
||||
encoders.clear();
|
||||
return EnumDevices(KSCATEGORY_ENCODER,
|
||||
EnumDeviceCallback(EnumVideoEncoder), &encoders);
|
||||
}
|
||||
|
||||
};
|
||||
411
obs-studio/deps/libdshowcapture/src/source/encoder.cpp
Normal file
411
obs-studio/deps/libdshowcapture/src/source/encoder.cpp
Normal file
@ -0,0 +1,411 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Lain Bailey <lain@obsproject.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
#include "encoder.hpp"
|
||||
#include "log.hpp"
|
||||
#include "avermedia-encode.h"
|
||||
|
||||
namespace DShow {
|
||||
|
||||
HVideoEncoder::HVideoEncoder()
|
||||
{
|
||||
initialized = CreateFilterGraph(&graph, &builder, &control);
|
||||
}
|
||||
|
||||
HVideoEncoder::~HVideoEncoder()
|
||||
{
|
||||
ComPtr<IEnumFilters> filterEnum;
|
||||
IBaseFilter *filter;
|
||||
HRESULT hr;
|
||||
|
||||
if (!initialized)
|
||||
return;
|
||||
|
||||
if (active)
|
||||
control->Stop();
|
||||
|
||||
/* seems like you have to manually release the entire graph otherwise
|
||||
* the encoder device might not end up releasing properly */
|
||||
hr = graph->EnumFilters(&filterEnum);
|
||||
if (hr == S_OK) {
|
||||
while (filterEnum->Next(1, &filter, nullptr) == S_OK) {
|
||||
graph->RemoveFilter(filter);
|
||||
filterEnum->Reset();
|
||||
filter->Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool HVideoEncoder::ConnectFilters()
|
||||
{
|
||||
ComPtr<IPin> deviceIn;
|
||||
ComPtr<IPin> deviceOut;
|
||||
ComPtr<IPin> encoderIn;
|
||||
ComPtr<IPin> encoderOut;
|
||||
bool success;
|
||||
HRESULT hr;
|
||||
|
||||
success = GetPinByName(device, PINDIR_INPUT, L"YUV In", &deviceIn);
|
||||
if (!success) {
|
||||
Warning(L"Failed to get YUV In pin");
|
||||
return false;
|
||||
}
|
||||
|
||||
success = GetPinByName(device, PINDIR_OUTPUT, L"Virtual Video Out",
|
||||
&deviceOut);
|
||||
if (!success) {
|
||||
Warning(L"Failed to get Virtual Video Out pin");
|
||||
return false;
|
||||
}
|
||||
|
||||
success = GetPinByName(encoder, PINDIR_INPUT, L"Virtual Video In",
|
||||
&encoderIn);
|
||||
if (!success) {
|
||||
Warning(L"Failed to get encoder input pin");
|
||||
return false;
|
||||
}
|
||||
|
||||
success = GetPinByName(encoder, PINDIR_OUTPUT, nullptr, &encoderOut);
|
||||
if (!success) {
|
||||
Warning(L"Failed to get encoder output pin");
|
||||
return false;
|
||||
}
|
||||
|
||||
hr = graph->ConnectDirect(output->GetPin(), deviceIn, nullptr);
|
||||
if (FAILED(hr)) {
|
||||
WarningHR(L"Failed to connect output to device", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
hr = graph->ConnectDirect(deviceOut, encoderIn, nullptr);
|
||||
if (FAILED(hr)) {
|
||||
WarningHR(L"Failed to connect device to encoder", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
hr = graph->ConnectDirect(encoderOut, capture->GetPin(), nullptr);
|
||||
if (FAILED(hr)) {
|
||||
WarningHR(L"Failed to connect encoder to capture", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool GetPinFirstMediaType(IPin *pin, AM_MEDIA_TYPE **mt)
|
||||
{
|
||||
ComPtr<IEnumMediaTypes> mediaEnum;
|
||||
HRESULT hr;
|
||||
ULONG fetched;
|
||||
|
||||
hr = pin->EnumMediaTypes(&mediaEnum);
|
||||
if (FAILED(hr)) {
|
||||
Warning(L"Failed to get pin media type enum");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mediaEnum->Next(1, mt, &fetched) != S_OK) {
|
||||
Warning(L"Failed to get pin media type");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HVideoEncoder::SetupCrossbar()
|
||||
{
|
||||
ComPtr<IBaseFilter> crossbar;
|
||||
ComPtr<IPin> pin;
|
||||
REGPINMEDIUM medium;
|
||||
|
||||
/* C353 has no crossbar */
|
||||
if (config.name.find(L"C353") != std::string::npos)
|
||||
return true;
|
||||
|
||||
if (!GetPinByName(device, PINDIR_INPUT, L"Analog Video In", &pin)) {
|
||||
Warning(L"Failed to get Analog Video In pin");
|
||||
return false;
|
||||
}
|
||||
if (!GetPinMedium(pin, medium)) {
|
||||
Warning(L"Failed to get Analog Video In pin medium");
|
||||
return false;
|
||||
}
|
||||
if (!GetFilterByMedium(AM_KSCATEGORY_CROSSBAR, medium, &crossbar)) {
|
||||
Warning(L"Failed to get crossbar filter");
|
||||
return false;
|
||||
}
|
||||
|
||||
graph->AddFilter(crossbar, L"Crossbar Filter");
|
||||
|
||||
if (!DirectConnectFilters(graph, crossbar, device)) {
|
||||
Warning(L"Failed to connect crossbar to device");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HVideoEncoder::SetupEncoder(IBaseFilter *filter)
|
||||
{
|
||||
ComPtr<IBaseFilter> deviceFilter;
|
||||
ComPtr<IPin> inputPin;
|
||||
ComPtr<IPin> outputPin;
|
||||
REGPINMEDIUM medium;
|
||||
MediaTypePtr mtRaw;
|
||||
MediaTypePtr mtEncoded;
|
||||
|
||||
if (!GetPinByName(filter, PINDIR_INPUT, nullptr, &inputPin)) {
|
||||
Warning(L"Could not get encoder input pin");
|
||||
return false;
|
||||
}
|
||||
if (!GetPinByName(filter, PINDIR_OUTPUT, nullptr, &outputPin)) {
|
||||
Warning(L"Could not get encoder output pin");
|
||||
return false;
|
||||
}
|
||||
if (!GetPinMedium(inputPin, medium)) {
|
||||
Warning(L"Could not get input pin medium");
|
||||
return false;
|
||||
}
|
||||
|
||||
inputPin.Release();
|
||||
|
||||
if (!GetFilterByMedium(CLSID_VideoInputDeviceCategory, medium,
|
||||
&deviceFilter)) {
|
||||
Warning(L"Could not get device filter from medium");
|
||||
return false;
|
||||
}
|
||||
if (!GetPinByName(deviceFilter, PINDIR_INPUT, L"YUV In", &inputPin)) {
|
||||
Warning(L"Could not device YUV pin");
|
||||
return false;
|
||||
}
|
||||
if (!GetPinFirstMediaType(inputPin, &mtRaw)) {
|
||||
Warning(L"Could not get YUV pin media type");
|
||||
return false;
|
||||
}
|
||||
if (!GetPinFirstMediaType(outputPin, &mtEncoded)) {
|
||||
Warning(L"Could not get encoder output pin media type");
|
||||
return false;
|
||||
}
|
||||
|
||||
PinCaptureInfo captureInfo;
|
||||
captureInfo.callback = [this](IMediaSample *s) { Receive(s); };
|
||||
captureInfo.expectedMajorType = mtEncoded->majortype;
|
||||
captureInfo.expectedSubType = mtEncoded->subtype;
|
||||
|
||||
long long frameTime;
|
||||
frameTime = config.fpsDenominator;
|
||||
frameTime *= 10000000;
|
||||
frameTime /= config.fpsNumerator;
|
||||
|
||||
encoder = filter;
|
||||
device = std::move(deviceFilter);
|
||||
capture = new CaptureFilter(captureInfo);
|
||||
output = new OutputFilter(VideoFormat::YV12, config.cx, config.cy,
|
||||
frameTime);
|
||||
|
||||
graph->AddFilter(output, nullptr);
|
||||
graph->AddFilter(device, L"Device Filter");
|
||||
graph->AddFilter(encoder, L"Encoder Filter");
|
||||
graph->AddFilter(capture, nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void Clamp(ULONG &val, ULONG minVal, ULONG maxVal)
|
||||
{
|
||||
if (val < minVal)
|
||||
val = minVal;
|
||||
else if (val > maxVal)
|
||||
val = maxVal;
|
||||
}
|
||||
|
||||
HRESULT SetAVMEncoderSetting(IKsPropertySet *propertySet, ULONG setting,
|
||||
ULONG param1, ULONG param2)
|
||||
{
|
||||
AVER_PARAMETERS params = {};
|
||||
params.ulIndex = setting;
|
||||
params.ulParam1 = param1;
|
||||
params.ulParam2 = param2;
|
||||
|
||||
if (setting == AVER_PARAMETER_ENCODE_FRAME_RATE) {
|
||||
Clamp(param1, 15, 60);
|
||||
} else if (setting == AVER_PARAMETER_ENCODE_BIT_RATE) {
|
||||
Clamp(param1, 1000, 60000);
|
||||
} else if (setting == AVER_PARAMETER_CURRENT_RESOLUTION) {
|
||||
Clamp(param1, 1, 30);
|
||||
}
|
||||
|
||||
return propertySet->Set(AVER_HW_ENCODE_PROPERTY,
|
||||
PROPERTY_HW_ENCODE_PARAMETER, ¶ms,
|
||||
sizeof(params), ¶ms, sizeof(params));
|
||||
}
|
||||
|
||||
bool SetAvermediaEncoderConfig(IBaseFilter *encoder, VideoEncoderConfig &config)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
ComQIPtr<IKsPropertySet> propertySet(encoder);
|
||||
if (!propertySet) {
|
||||
Warning(L"Could not get IKsPropertySet for encoder");
|
||||
return false;
|
||||
}
|
||||
|
||||
double fps =
|
||||
double(config.fpsNumerator) / double(config.fpsDenominator);
|
||||
|
||||
hr = SetAVMEncoderSetting(propertySet, AVER_PARAMETER_ENCODE_FRAME_RATE,
|
||||
ULONG(fps), 0);
|
||||
if (FAILED(hr)) {
|
||||
WarningHR(L"Failed to set Avermedia encoder FPS", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
hr = SetAVMEncoderSetting(propertySet, AVER_PARAMETER_ENCODE_BIT_RATE,
|
||||
ULONG(config.bitrate), 0);
|
||||
if (FAILED(hr)) {
|
||||
WarningHR(L"Failed to set Avermedia encoder bitrate", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
hr = SetAVMEncoderSetting(propertySet,
|
||||
AVER_PARAMETER_CURRENT_RESOLUTION,
|
||||
ULONG(config.cx), ULONG(config.cy));
|
||||
if (FAILED(hr)) {
|
||||
WarningHR(L"Failed to set Avermedia encoder current res", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
hr = SetAVMEncoderSetting(propertySet, AVER_PARAMETER_ENCODE_RESOLUTION,
|
||||
ULONG(config.cx), ULONG(config.cy));
|
||||
if (FAILED(hr)) {
|
||||
WarningHR(L"Failed to set Avermedia encoder res", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
hr = SetAVMEncoderSetting(propertySet, AVER_PARAMETER_ENCODE_GOP,
|
||||
ULONG(config.keyframeInterval), 0);
|
||||
if (FAILED(hr)) {
|
||||
WarningHR(L"Failed to set Avermedia encoder GOP", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HVideoEncoder::SetConfig(VideoEncoderConfig &config)
|
||||
{
|
||||
ComPtr<IBaseFilter> filter;
|
||||
ComPtr<IBaseFilter> crossbar;
|
||||
|
||||
if (config.name.empty() && config.path.empty()) {
|
||||
Warning(L"No video encoder name or path specified");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = GetDeviceFilter(KSCATEGORY_ENCODER, config.name.c_str(),
|
||||
config.path.c_str(), &filter);
|
||||
if (!success) {
|
||||
Warning(L"Video encoder '%s': %s not found",
|
||||
config.name.c_str(), config.path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!filter) {
|
||||
Warning(L"Could not get encoder filter");
|
||||
return false;
|
||||
}
|
||||
|
||||
this->config = config;
|
||||
|
||||
if (!SetupEncoder(filter)) {
|
||||
Warning(L"Failed to set up encoder");
|
||||
return false;
|
||||
}
|
||||
if (!SetupCrossbar()) {
|
||||
Warning(L"Failed to set up crossbar");
|
||||
return false;
|
||||
}
|
||||
if (!SetAvermediaEncoderConfig(device, config)) {
|
||||
Warning(L"Failed to set Avermedia encoder settings");
|
||||
return false;
|
||||
}
|
||||
if (!ConnectFilters()) {
|
||||
Warning(L"Failed to connect encoder filters");
|
||||
return false;
|
||||
}
|
||||
|
||||
LogFilters(graph);
|
||||
|
||||
HRESULT hr = control->Run();
|
||||
if (FAILED(hr)) {
|
||||
WarningHR(L"Run failed", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
active = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void HVideoEncoder::Receive(IMediaSample *s)
|
||||
{
|
||||
BYTE *data;
|
||||
size_t size;
|
||||
|
||||
if (FAILED(s->GetPointer(&data)))
|
||||
return;
|
||||
|
||||
size = (size_t)s->GetActualDataLength();
|
||||
if (!size)
|
||||
return;
|
||||
|
||||
packetMutex.lock();
|
||||
packets.emplace_back(data, size);
|
||||
packetMutex.unlock();
|
||||
}
|
||||
|
||||
bool HVideoEncoder::Encode(unsigned char *data[DSHOW_MAX_PLANES],
|
||||
size_t linesize[DSHOW_MAX_PLANES],
|
||||
long long timestampStart, long long timestampEnd,
|
||||
EncoderPacket &packet, bool &new_packet)
|
||||
{
|
||||
new_packet = false;
|
||||
|
||||
if (!active)
|
||||
return false;
|
||||
|
||||
output->Send(data, linesize, timestampStart, timestampEnd);
|
||||
ptsVals.push_back(timestampStart);
|
||||
|
||||
packetMutex.lock();
|
||||
if (packets.size() > 0) {
|
||||
curPacket = move(packets.front());
|
||||
long long ptsOut = ptsVals[0];
|
||||
packets.pop_front();
|
||||
ptsVals.pop_front();
|
||||
|
||||
packet.data = curPacket.data.data();
|
||||
packet.size = curPacket.data.size();
|
||||
packet.pts = ptsOut;
|
||||
packet.dts = ptsOut;
|
||||
new_packet = true;
|
||||
}
|
||||
packetMutex.unlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
86
obs-studio/deps/libdshowcapture/src/source/encoder.hpp
Normal file
86
obs-studio/deps/libdshowcapture/src/source/encoder.hpp
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Lain Bailey <lain@obsproject.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../dshowcapture.hpp"
|
||||
#include "output-filter.hpp"
|
||||
#include "capture-filter.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
using namespace std;
|
||||
|
||||
struct EncodedData {
|
||||
vector<unsigned char> data;
|
||||
|
||||
inline EncodedData() = default;
|
||||
|
||||
inline EncodedData(unsigned char *data_, size_t size)
|
||||
{
|
||||
data.resize(size);
|
||||
memcpy(data.data(), data_, size);
|
||||
}
|
||||
};
|
||||
|
||||
namespace DShow {
|
||||
|
||||
struct HVideoEncoder {
|
||||
ComPtr<IGraphBuilder> graph;
|
||||
ComPtr<ICaptureGraphBuilder2> builder;
|
||||
ComPtr<IMediaControl> control;
|
||||
|
||||
ComPtr<IBaseFilter> encoder;
|
||||
ComPtr<IBaseFilter> device;
|
||||
ComPtr<OutputFilter> output;
|
||||
ComPtr<CaptureFilter> capture;
|
||||
|
||||
VideoEncoderConfig config;
|
||||
|
||||
mutex packetMutex;
|
||||
deque<EncodedData> packets;
|
||||
EncodedData curPacket;
|
||||
|
||||
deque<long long> ptsVals;
|
||||
|
||||
bool initialized = false;
|
||||
bool active = false;
|
||||
|
||||
HVideoEncoder();
|
||||
~HVideoEncoder();
|
||||
|
||||
bool SetupCrossbar();
|
||||
|
||||
void Receive(IMediaSample *s);
|
||||
|
||||
bool ConnectFilters();
|
||||
|
||||
bool SetupEncoder(IBaseFilter *filter);
|
||||
|
||||
bool SetConfig(VideoEncoderConfig &config);
|
||||
|
||||
bool Encode(unsigned char *frame[DSHOW_MAX_PLANES],
|
||||
size_t linesize[DSHOW_MAX_PLANES], long long timestampStart,
|
||||
long long timestampEnd, EncoderPacket &packet,
|
||||
bool &new_packet);
|
||||
};
|
||||
|
||||
};
|
||||
3
obs-studio/deps/libdshowcapture/src/source/external/.clang-format
vendored
Normal file
3
obs-studio/deps/libdshowcapture/src/source/external/.clang-format
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
Language: Cpp
|
||||
SortIncludes: false
|
||||
DisableFormat: true
|
||||
238
obs-studio/deps/libdshowcapture/src/source/external/IVideoCaptureFilter.h
vendored
Normal file
238
obs-studio/deps/libdshowcapture/src/source/external/IVideoCaptureFilter.h
vendored
Normal file
@ -0,0 +1,238 @@
|
||||
//=============================================================================
|
||||
//! @file IVideoCaptureFilter.h
|
||||
//! @bc -----------------------------------------------------------------------
|
||||
//! @ec @brief Interface declaration for Elgato Video Capture Filter
|
||||
//! @author F.M.Birth, T.Schnitzler
|
||||
//! @date 01-Oct-12 FMB - Creation
|
||||
//! @date 08-Apr-13 TS - Added IVideoCaptureFilter2
|
||||
//! @date 14-Nov-13 TS - Added IVideoCaptureFilter3
|
||||
//! @date 21-Jul-14 TS - Added IVideoCaptureFilter4
|
||||
//! @date 12-Aug-14 TS - Added IVideoCaptureFilter5
|
||||
//! @date 28-Aug-14 FDj - MIT license added
|
||||
//! @note Supports Elgato Game Capture HD
|
||||
//! @bc -----------------------------------------------------------------------
|
||||
//! @ec @par Copyright
|
||||
//! @n (c) 2012-14, Elgato Systems. All Rights Reserved.
|
||||
//! @n
|
||||
//! @n The MIT License (MIT)
|
||||
//! @n
|
||||
//! @n Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//! @n of this software and associated documentation files (the "Software"), to deal
|
||||
//! @n in the Software without restriction, including without limitation the rights
|
||||
//! @n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//! @n copies of the Software, and to permit persons to whom the Software is
|
||||
//! @n furnished to do so, subject to the following conditions:
|
||||
//! @n
|
||||
//! @n The above copyright notice and this permission notice shall be included in all
|
||||
//! @n copies or substantial portions of the Software.
|
||||
//! @n
|
||||
//! @n THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//! @n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//! @n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//! @n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//! @n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//! @n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//! @n SOFTWARE.
|
||||
//! @n
|
||||
//=============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
/*=============================================================================
|
||||
// FILTER INTERFACE
|
||||
=============================================================================*/
|
||||
#define VIDEO_CAPTURE_FILTER_NAME "Elgato Game Capture HD"
|
||||
#define VIDEO_CAPTURE_FILTER_NAME_L L"Elgato Game Capture HD"
|
||||
|
||||
// {39F50F4C-99E1-464a-B6F9-D605B4FB5918}
|
||||
DEFINE_GUID(CLSID_ElgatoVideoCaptureFilter,
|
||||
0x39f50f4c, 0x99e1, 0x464a, 0xb6, 0xf9, 0xd6, 0x5, 0xb4, 0xfb, 0x59, 0x18);
|
||||
|
||||
// {39F50F4C-99E1-464a-B6F9-D605B4FB5919}
|
||||
DEFINE_GUID(CLSID_ElgatoVideoCaptureFilterProperties,
|
||||
0x39f50f4c, 0x99e1, 0x464a, 0xb6, 0xf9, 0xd6, 0x5, 0xb4, 0xfb, 0x59, 0x19);
|
||||
|
||||
// {39F50F4C-99E1-464a-B6F9-D605B4FB5920}
|
||||
DEFINE_GUID(IID_IElgatoVideoCaptureFilter,
|
||||
0x39f50f4c, 0x99e1, 0x464a, 0xb6, 0xf9, 0xd6, 0x5, 0xb4, 0xfb, 0x59, 0x20);
|
||||
|
||||
// {585B2914-252E-49bd-B730-7B4C40F4D4E5}
|
||||
DEFINE_GUID(IID_IElgatoVideoCaptureFilter2,
|
||||
0x585b2914, 0x252e, 0x49bd, 0xb7, 0x30, 0x7b, 0x4c, 0x40, 0xf4, 0xd4, 0xe5);
|
||||
|
||||
// {CC415EB7-B1C7-428c-9E5E-D9747DB4BE76}
|
||||
DEFINE_GUID(IID_IElgatoVideoCaptureFilter3,
|
||||
0xcc415eb7, 0xb1c7, 0x428c, 0x9e, 0x5e, 0xd9, 0x74, 0x7d, 0xb4, 0xbe, 0x76);
|
||||
|
||||
// {197992FF-ED65-47CB-8032-D287AB40B33F}
|
||||
DEFINE_GUID(IID_IElgatoVideoCaptureFilter4,
|
||||
0x197992ff, 0xed65, 0x47cb, 0x80, 0x32, 0xd2, 0x87, 0xab, 0x40, 0xb3, 0x3f);
|
||||
|
||||
// {7E6E9E9E-4062-4364-99B1-15C2F662B502}
|
||||
DEFINE_GUID(IID_IElgatoVideoCaptureFilter5,
|
||||
0x7e6e9e9e, 0x4062, 0x4364, 0x99, 0xb1, 0x15, 0xc2, 0xf6, 0x62, 0xb5, 0x2);
|
||||
|
||||
/*=============================================================================
|
||||
// IElgatoVideoCaptureFilter
|
||||
=============================================================================*/
|
||||
|
||||
//! Interface
|
||||
DECLARE_INTERFACE_(IElgatoVideoCaptureFilter, IUnknown)
|
||||
{
|
||||
};
|
||||
|
||||
/*=============================================================================
|
||||
// IElgatoVideoCaptureFilter2
|
||||
=============================================================================*/
|
||||
|
||||
//! Video Capture device type
|
||||
enum VIDEO_CAPTURE_FILTER_DEVICE_TYPE
|
||||
{
|
||||
VIDEO_CAPTURE_FILTER_DEVICE_TYPE_INVALID = 0, //!< Invalid
|
||||
VIDEO_CAPTURE_FILTER_DEVICE_TYPE_GAME_CAPTURE_HD = 2, //!< Game Capture HD (VID: 0x0fd9 PID: 0x0044, 0x004e, 0x0051)
|
||||
VIDEO_CAPTURE_FILTER_DEVICE_TYPE_GAME_CAPTURE_HD60 = 8, //!< Game Capture HD60 (VID: 0x0fd9 PID: 0x005c)
|
||||
NUM_VIDEO_CAPTURE_FILTER_DEVICE_TYPE
|
||||
};
|
||||
|
||||
//! Input device
|
||||
enum VIDEO_CAPTURE_FILTER_INPUT_DEVICE
|
||||
{
|
||||
VIDEO_CAPTURE_FILTER_INPUT_DEVICE_INVALID = 0, //!< Invalid
|
||||
VIDEO_CAPTURE_FILTER_INPUT_DEVICE_XBOX360 = 1, //!< Microsoft Xbox 360
|
||||
VIDEO_CAPTURE_FILTER_INPUT_DEVICE_PLAYSTATION3 = 2, //!< Sony PlayStation 3
|
||||
VIDEO_CAPTURE_FILTER_INPUT_DEVICE_IPAD = 3, //!< Apple iPad
|
||||
VIDEO_CAPTURE_FILTER_INPUT_DEVICE_IPOD_IPHONE = 4, //!< Apple iPod or iPhone
|
||||
VIDEO_CAPTURE_FILTER_INPUT_DEVICE_WII = 5, //!< Nintendo Wii
|
||||
VIDEO_CAPTURE_FILTER_INPUT_DEVICE_OTHER = 6, //!< Other
|
||||
VIDEO_CAPTURE_FILTER_INPUT_DEVICE_WII_U = 7, //!< Nintendo Wii U
|
||||
VIDEO_CAPTURE_FILTER_INPUT_DEVICE_XBOX_ONE = 8, //!< Microsoft Xbox One
|
||||
VIDEO_CAPTURE_FILTER_INPUT_DEVICE_PLAYSTATION4 = 9, //!< Sony PlayStation 4
|
||||
};
|
||||
|
||||
//! Video inputs
|
||||
enum VIDEO_CAPTURE_FILTER_VIDEO_INPUT
|
||||
{
|
||||
VIDEO_CAPTURE_FILTER_VIDEO_INPUT_INVALID = 0, //!< Invalid
|
||||
VIDEO_CAPTURE_FILTER_VIDEO_INPUT_COMPOSITE = 1, //!< Composite
|
||||
VIDEO_CAPTURE_FILTER_VIDEO_INPUT_SVIDEO = 2, //!< S-Video
|
||||
VIDEO_CAPTURE_FILTER_VIDEO_INPUT_COMPONENT = 3, //!< Component
|
||||
VIDEO_CAPTURE_FILTER_VIDEO_INPUT_HDMI = 4, //!< HDMI
|
||||
};
|
||||
|
||||
//! Video encoder profile
|
||||
enum VIDEO_CAPTURE_FILTER_VID_ENC_PROFILE
|
||||
{
|
||||
VIDEO_CAPTURE_FILTER_VID_ENC_PROFILE_INVALID = 0x00000000, //!< Invalid
|
||||
VIDEO_CAPTURE_FILTER_VID_ENC_PROFILE_240 = 0x00000001, //!< 320x240
|
||||
VIDEO_CAPTURE_FILTER_VID_ENC_PROFILE_360 = 0x00000002, //!< 480x360
|
||||
VIDEO_CAPTURE_FILTER_VID_ENC_PROFILE_480 = 0x00000004, //!< 640x480
|
||||
VIDEO_CAPTURE_FILTER_VID_ENC_PROFILE_720 = 0x00000008, //!< 1280x720
|
||||
VIDEO_CAPTURE_FILTER_VID_ENC_PROFILE_1080 = 0x00000010, //!< 1920x1080
|
||||
};
|
||||
|
||||
//! Color range
|
||||
enum VIDEO_CAPTURE_FILTER_COLOR_RANGE
|
||||
{
|
||||
VIDEO_CAPTURE_FILTER_COLOR_RANGE_INVALID = 0, //!< Invalid
|
||||
VIDEO_CAPTURE_FILTER_COLOR_RANGE_FULL = 1, //!< 0-255
|
||||
VIDEO_CAPTURE_FILTER_COLOR_RANGE_LIMITED = 2, //!< 16-235
|
||||
VIDEO_CAPTURE_FILTER_COLOR_RANGE_SHOOT = 3, //!<
|
||||
};
|
||||
|
||||
//! Settings
|
||||
struct VIDEO_CAPTURE_FILTER_SETTINGS
|
||||
{
|
||||
TCHAR deviceName[256]; //!< Device name (get only)
|
||||
VIDEO_CAPTURE_FILTER_INPUT_DEVICE inputDevice; //!< Input device (e.g. Xbox360)
|
||||
VIDEO_CAPTURE_FILTER_VIDEO_INPUT videoInput; //!< Video input (e.g. HDMI)
|
||||
VIDEO_CAPTURE_FILTER_VID_ENC_PROFILE profile; //!< Video encoder profile (maximum resolution)
|
||||
BOOL useAnalogAudioInput; //!< for HDMI with analog audio input
|
||||
VIDEO_CAPTURE_FILTER_COLOR_RANGE hdmiColorRange; //!< HDMI color range
|
||||
int brightness; //!< Brightness (0-10000)
|
||||
int contrast; //!< Contrast (0-10000)
|
||||
int saturation; //!< Saturation (0-10000)
|
||||
int hue; //!< Hue (0-10000)
|
||||
int analogAudioGain; //!< Analog audio gain (-60 - 12 dB)
|
||||
int digitalAudioGain; //!< Digital audio gain (-60 - 12 dB)
|
||||
BOOL preserveInputFormat; //!< Input Format will be preserved (e.g. do not convert interlaced to progressive)
|
||||
BOOL stretchStandardDefinitionInput; //!< Stretch SD input to 16:9
|
||||
};
|
||||
|
||||
//! Interface
|
||||
DECLARE_INTERFACE_(IElgatoVideoCaptureFilter2, IElgatoVideoCaptureFilter)
|
||||
{
|
||||
// Get current settings
|
||||
STDMETHOD(GetSettings)(THIS_ VIDEO_CAPTURE_FILTER_SETTINGS *pSettings) PURE;
|
||||
|
||||
// Set settings
|
||||
STDMETHOD(SetSettings)(THIS_ const VIDEO_CAPTURE_FILTER_SETTINGS *pcSettings) PURE;
|
||||
};
|
||||
|
||||
/*=============================================================================
|
||||
// IElgatoVideoCaptureFilter3
|
||||
=============================================================================*/
|
||||
|
||||
//! Interface
|
||||
DECLARE_INTERFACE_(IElgatoVideoCaptureFilter3, IElgatoVideoCaptureFilter2)
|
||||
{
|
||||
//! Get A/V delay in milli-seconds (approximate delay between input signal and DirectShow
|
||||
//! filter output)
|
||||
STDMETHOD(GetDelayMs)(THIS_ int* pnDelayMs) PURE;
|
||||
};
|
||||
|
||||
/*=============================================================================
|
||||
// IElgatoVideoCaptureFilter4
|
||||
=============================================================================*/
|
||||
|
||||
//! Messages
|
||||
enum VIDEO_CAPTURE_FILTER_NOTIFICATION
|
||||
{
|
||||
//! Description: Delay of the device has changed. Call GetDelayMs() to get the new delay.
|
||||
VIDEO_CAPTURE_FILTER_NOTIFICATION_DEVICE_DELAY_CHANGED = 110, //!< Data: none
|
||||
|
||||
//! Description: Output format has changed. Update your signal path accordingly.
|
||||
VIDEO_CAPTURE_FILTER_NOTIFICATION_CAPTURE_OUTPUT_FORMAT_CHANGED = 305, //!< Data: none
|
||||
};
|
||||
|
||||
//! Custom event that can be received by IMediaEvent::GetEvent. If SetNotificationCallback() was not set this method is used to send notifications.
|
||||
//! lEventCode = VIDEO_CAPTURE_FILTER_EVENT
|
||||
//! lParam1 = VIDEO_CAPTURE_FILTER_NOTIFICATION
|
||||
//! lParam2 = reserved for future use (e.g. notifications with additional data)
|
||||
#define VIDEO_CAPTURE_FILTER_EVENT EC_USER + 0x0FD9
|
||||
|
||||
//! Message callback
|
||||
typedef void (CALLBACK* PFN_VIDEO_CAPTURE_FILTER_NOTIFICATION_CALLBACK)(VIDEO_CAPTURE_FILTER_NOTIFICATION nMessage, void* pData, int nSize, void* pContext);
|
||||
|
||||
//! Interface
|
||||
DECLARE_INTERFACE_(IElgatoVideoCaptureFilter4, IElgatoVideoCaptureFilter3)
|
||||
{
|
||||
//! Check device is present
|
||||
STDMETHOD(GetDevicePresent)(THIS_ BOOL* pfDevicePresent) PURE;
|
||||
|
||||
//! Get current device type
|
||||
STDMETHOD(GetDeviceType)(THIS_ VIDEO_CAPTURE_FILTER_DEVICE_TYPE* pnDeviceType) PURE;
|
||||
|
||||
//! Set callback to receive notifications
|
||||
STDMETHOD(SetNotificationCallback)(THIS_ PFN_VIDEO_CAPTURE_FILTER_NOTIFICATION_CALLBACK pCallback, void* pContext) PURE;
|
||||
};
|
||||
|
||||
/*=============================================================================
|
||||
// IElgatoVideoCaptureFilter5
|
||||
=============================================================================*/
|
||||
|
||||
//! Extended Settings
|
||||
struct VIDEO_CAPTURE_FILTER_SETTINGS_EX
|
||||
{
|
||||
VIDEO_CAPTURE_FILTER_SETTINGS Settings;
|
||||
BOOL enableFullFrameRate; //!< Enable full frame rate (50/60 fps)
|
||||
BYTE reserved[20 * 1024];
|
||||
};
|
||||
|
||||
DECLARE_INTERFACE_(IElgatoVideoCaptureFilter5, IElgatoVideoCaptureFilter4)
|
||||
{
|
||||
//! Get current settings
|
||||
STDMETHOD(GetSettingsEx)(THIS_ VIDEO_CAPTURE_FILTER_SETTINGS_EX *pSettings) PURE;
|
||||
|
||||
//! Set settings
|
||||
STDMETHOD(SetSettingsEx)(THIS_ const VIDEO_CAPTURE_FILTER_SETTINGS_EX *pcSettings) PURE;
|
||||
};
|
||||
96
obs-studio/deps/libdshowcapture/src/source/log.cpp
Normal file
96
obs-studio/deps/libdshowcapture/src/source/log.cpp
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Lain Bailey <lain@obsproject.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
#include "dshow-base.hpp"
|
||||
#include "log.hpp"
|
||||
#include "../dshowcapture.hpp"
|
||||
|
||||
namespace DShow {
|
||||
|
||||
void *logParam = NULL;
|
||||
static LogCallback logCallback = NULL;
|
||||
|
||||
void SetLogCallback(LogCallback callback, void *param)
|
||||
{
|
||||
logCallback = callback;
|
||||
logParam = param;
|
||||
}
|
||||
|
||||
static void Log(LogType type, const wchar_t *format, va_list args)
|
||||
{
|
||||
wchar_t str[4096];
|
||||
vswprintf_s(str, 4096, format, args);
|
||||
|
||||
if (logCallback)
|
||||
logCallback(type, str, logParam);
|
||||
}
|
||||
|
||||
void Error(const wchar_t *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
Log(LogType::Error, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void Warning(const wchar_t *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
Log(LogType::Warning, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void Info(const wchar_t *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
Log(LogType::Info, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void Debug(const wchar_t *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
Log(LogType::Debug, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void ErrorHR(const wchar_t *str, HRESULT hr)
|
||||
{
|
||||
Error(L"%s (0x%08lX): %s", str, hr, ConvertHRToEnglish(hr).c_str());
|
||||
}
|
||||
|
||||
void WarningHR(const wchar_t *str, HRESULT hr)
|
||||
{
|
||||
Warning(L"%s (0x%08lX): %s", str, hr, ConvertHRToEnglish(hr).c_str());
|
||||
}
|
||||
|
||||
void InfoHR(const wchar_t *str, HRESULT hr)
|
||||
{
|
||||
Info(L"%s (0x%08lX): %s", str, hr, ConvertHRToEnglish(hr).c_str());
|
||||
}
|
||||
|
||||
void DebugHR(const wchar_t *str, HRESULT hr)
|
||||
{
|
||||
Debug(L"%s (0x%08lX): %s", str, hr, ConvertHRToEnglish(hr).c_str());
|
||||
}
|
||||
|
||||
}; /* namespace DShow */
|
||||
37
obs-studio/deps/libdshowcapture/src/source/log.hpp
Normal file
37
obs-studio/deps/libdshowcapture/src/source/log.hpp
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Lain Bailey <lain@obsproject.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include "windows.h"
|
||||
|
||||
namespace DShow {
|
||||
|
||||
void Error(const wchar_t *format, ...);
|
||||
void Warning(const wchar_t *format, ...);
|
||||
void Info(const wchar_t *format, ...);
|
||||
void Debug(const wchar_t *format, ...);
|
||||
|
||||
void ErrorHR(const wchar_t *str, HRESULT hr);
|
||||
void WarningHR(const wchar_t *str, HRESULT hr);
|
||||
void InfoHR(const wchar_t *str, HRESULT hr);
|
||||
void DebugHR(const wchar_t *str, HRESULT hr);
|
||||
|
||||
}; /* namespace DShow */
|
||||
992
obs-studio/deps/libdshowcapture/src/source/output-filter.cpp
Normal file
992
obs-studio/deps/libdshowcapture/src/source/output-filter.cpp
Normal file
@ -0,0 +1,992 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Lain Bailey <lain@obsproject.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
#include "output-filter.hpp"
|
||||
#include "dshow-formats.hpp"
|
||||
#include "log.hpp"
|
||||
|
||||
#include <strsafe.h>
|
||||
|
||||
namespace DShow {
|
||||
|
||||
#if 0
|
||||
#define PrintFunc(x) Debug(x)
|
||||
#else
|
||||
#define PrintFunc(x)
|
||||
#endif
|
||||
|
||||
#define FILTER_NAME L"Output Filter"
|
||||
#define VIDEO_PIN_NAME L"Video Output"
|
||||
#define AUDIO_PIN_NAME L"Audio Output"
|
||||
|
||||
OutputPin::OutputPin(OutputFilter *filter_) : refCount(0), filter(filter_) {}
|
||||
|
||||
OutputPin::OutputPin(OutputFilter *filter_, VideoFormat format, int cx, int cy,
|
||||
long long interval)
|
||||
: OutputPin(filter_)
|
||||
{
|
||||
curCX = cx;
|
||||
curCY = cy;
|
||||
curInterval = interval;
|
||||
|
||||
AddVideoFormat(format, cx, cy, interval);
|
||||
SetVideoFormat(format, cx, cy, interval);
|
||||
}
|
||||
|
||||
OutputPin::~OutputPin() {}
|
||||
|
||||
STDMETHODIMP OutputPin::QueryInterface(REFIID riid, void **ppv)
|
||||
{
|
||||
if (riid == IID_IUnknown) {
|
||||
AddRef();
|
||||
*ppv = this;
|
||||
} else if (riid == IID_IPin) {
|
||||
AddRef();
|
||||
*ppv = (IPin *)this;
|
||||
} else if (riid == IID_IMemInputPin) {
|
||||
AddRef();
|
||||
*ppv = (IMemInputPin *)this;
|
||||
} else if (riid == IID_IAMStreamConfig) {
|
||||
AddRef();
|
||||
*ppv = (IAMStreamConfig *)this;
|
||||
return S_OK;
|
||||
} else if (riid == IID_IKsPropertySet) {
|
||||
AddRef();
|
||||
*ppv = (IKsPropertySet *)this;
|
||||
return S_OK;
|
||||
} else {
|
||||
*ppv = nullptr;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
STDMETHODIMP_(ULONG) OutputPin::AddRef()
|
||||
{
|
||||
return (ULONG)InterlockedIncrement(&refCount);
|
||||
}
|
||||
|
||||
STDMETHODIMP_(ULONG) OutputPin::Release()
|
||||
{
|
||||
long newRefs = InterlockedDecrement(&refCount);
|
||||
if (!newRefs) {
|
||||
delete this;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (ULONG)newRefs;
|
||||
}
|
||||
|
||||
// IPin methods
|
||||
STDMETHODIMP OutputPin::Connect(IPin *pReceivePin, const AM_MEDIA_TYPE *pmt)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
PrintFunc(L"OutputPin::Connect");
|
||||
|
||||
if (filter->state == State_Running)
|
||||
return VFW_E_NOT_STOPPED;
|
||||
|
||||
if (connectedPin)
|
||||
return VFW_E_ALREADY_CONNECTED;
|
||||
|
||||
hr = pReceivePin->ReceiveConnection(this, mt);
|
||||
if (FAILED(hr)) {
|
||||
#if 0 /* debug code to test caps on fail */
|
||||
ComPtr<IEnumMediaTypes> enumMT;
|
||||
pReceivePin->EnumMediaTypes(&enumMT);
|
||||
|
||||
if (enumMT) {
|
||||
MediaTypePtr mt;
|
||||
ULONG count = 0;
|
||||
|
||||
while (enumMT->Next(1, &mt, &count) == S_OK) {
|
||||
int test = 0;
|
||||
test = 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
if (!AllocateBuffers(pReceivePin, true)) {
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
connectedPin = pReceivePin;
|
||||
DSHOW_UNUSED(pmt);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP OutputPin::ReceiveConnection(IPin *pConnector,
|
||||
const AM_MEDIA_TYPE *pmt)
|
||||
{
|
||||
PrintFunc(L"OutputPin::ReceiveConnection");
|
||||
|
||||
DSHOW_UNUSED(pConnector);
|
||||
DSHOW_UNUSED(pmt);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP OutputPin::Disconnect()
|
||||
{
|
||||
PrintFunc(L"OutputPin::Disconnect");
|
||||
|
||||
if (!connectedPin)
|
||||
return S_FALSE;
|
||||
|
||||
if (!!allocator) {
|
||||
allocator->Decommit();
|
||||
allocator.Clear();
|
||||
}
|
||||
|
||||
connectedPin = nullptr;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP OutputPin::ConnectedTo(IPin **pPin)
|
||||
{
|
||||
PrintFunc(L"OutputPin::ConnectedTo");
|
||||
|
||||
if (!connectedPin) {
|
||||
*pPin = nullptr;
|
||||
return VFW_E_NOT_CONNECTED;
|
||||
}
|
||||
|
||||
IPin *pin = connectedPin;
|
||||
pin->AddRef();
|
||||
*pPin = pin;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP OutputPin::ConnectionMediaType(AM_MEDIA_TYPE *pmt)
|
||||
{
|
||||
PrintFunc(L"OutputPin::ConnectionMediaType");
|
||||
|
||||
if (!connectedPin)
|
||||
return VFW_E_NOT_CONNECTED;
|
||||
|
||||
return CopyMediaType(pmt, mt);
|
||||
}
|
||||
|
||||
STDMETHODIMP OutputPin::QueryPinInfo(PIN_INFO *pInfo)
|
||||
{
|
||||
PrintFunc(L"OutputPin::QueryPinInfo");
|
||||
|
||||
pInfo->pFilter = filter;
|
||||
if (filter) {
|
||||
IBaseFilter *ptr = filter;
|
||||
ptr->AddRef();
|
||||
}
|
||||
|
||||
if (mt->majortype == MEDIATYPE_Video)
|
||||
memcpy(pInfo->achName, VIDEO_PIN_NAME, sizeof(VIDEO_PIN_NAME));
|
||||
else
|
||||
memcpy(pInfo->achName, AUDIO_PIN_NAME, sizeof(AUDIO_PIN_NAME));
|
||||
|
||||
pInfo->dir = PINDIR_OUTPUT;
|
||||
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
STDMETHODIMP OutputPin::QueryDirection(PIN_DIRECTION *pPinDir)
|
||||
{
|
||||
*pPinDir = PINDIR_OUTPUT;
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
#define OUTPUT_PIN_NAME L"Output Pin"
|
||||
|
||||
STDMETHODIMP OutputPin::QueryId(LPWSTR *lpId)
|
||||
{
|
||||
wchar_t *str = (wchar_t *)CoTaskMemAlloc(sizeof(OUTPUT_PIN_NAME));
|
||||
memcpy(str, OUTPUT_PIN_NAME, sizeof(OUTPUT_PIN_NAME));
|
||||
*lpId = str;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP OutputPin::QueryAccept(const AM_MEDIA_TYPE *)
|
||||
{
|
||||
PrintFunc(L"OutputPin::QueryAccept");
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP OutputPin::EnumMediaTypes(IEnumMediaTypes **ppEnum)
|
||||
{
|
||||
PrintFunc(L"OutputPin::EnumMediaTypes");
|
||||
|
||||
*ppEnum = new OutputEnumMediaTypes(this);
|
||||
if (!*ppEnum)
|
||||
return E_OUTOFMEMORY;
|
||||
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
STDMETHODIMP OutputPin::QueryInternalConnections(IPin **apPin, ULONG *nPin)
|
||||
{
|
||||
PrintFunc(L"OutputPin::QueryInternalConnections");
|
||||
|
||||
DSHOW_UNUSED(apPin);
|
||||
DSHOW_UNUSED(nPin);
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
STDMETHODIMP OutputPin::EndOfStream()
|
||||
{
|
||||
PrintFunc(L"OutputPin::EndOfStream");
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP OutputPin::BeginFlush()
|
||||
{
|
||||
PrintFunc(L"OutputPin::BeginFlush");
|
||||
|
||||
flushing = true;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP OutputPin::EndFlush()
|
||||
{
|
||||
PrintFunc(L"OutputPin::EndFlush");
|
||||
|
||||
flushing = false;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP OutputPin::NewSegment(REFERENCE_TIME, REFERENCE_TIME, double)
|
||||
{
|
||||
PrintFunc(L"OutputPin::NewSegment");
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP OutputPin::GetFormat(AM_MEDIA_TYPE **ppmt)
|
||||
{
|
||||
PrintFunc(L"OutputPin::GetFormat");
|
||||
|
||||
if (!ppmt) {
|
||||
return E_POINTER;
|
||||
}
|
||||
|
||||
*ppmt = mt.Duplicate();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP OutputPin::GetNumberOfCapabilities(int *piCount, int *piSize)
|
||||
{
|
||||
PrintFunc(L"OutputPin::GetNumberOfCapabilities");
|
||||
|
||||
if (!piCount || !piSize) {
|
||||
return E_POINTER;
|
||||
}
|
||||
|
||||
*piCount = (int)mtList.size();
|
||||
*piSize = sizeof(VIDEO_STREAM_CONFIG_CAPS);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP OutputPin::GetStreamCaps(int iIndex, AM_MEDIA_TYPE **ppmt,
|
||||
BYTE *pSCC)
|
||||
{
|
||||
PrintFunc(L"OutputPin::GetStreamCaps");
|
||||
|
||||
int count = (int)mtList.size();
|
||||
|
||||
if (!ppmt || !pSCC) {
|
||||
return E_POINTER;
|
||||
}
|
||||
if (iIndex > (count - 1)) {
|
||||
return S_FALSE;
|
||||
}
|
||||
if (iIndex < 0) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
AM_MEDIA_TYPE *pmt = mtList[iIndex].Duplicate();
|
||||
VIDEOINFOHEADER *vih = reinterpret_cast<decltype(vih)>(pmt->pbFormat);
|
||||
|
||||
VIDEO_STREAM_CONFIG_CAPS caps = {};
|
||||
caps.guid = FORMAT_VideoInfo;
|
||||
caps.MinFrameInterval = vih->AvgTimePerFrame;
|
||||
caps.MaxFrameInterval = vih->AvgTimePerFrame;
|
||||
caps.MinOutputSize.cx = vih->bmiHeader.biWidth;
|
||||
caps.MinOutputSize.cy = vih->bmiHeader.biHeight;
|
||||
caps.MaxOutputSize = caps.MinOutputSize;
|
||||
caps.InputSize = caps.MinOutputSize;
|
||||
caps.MinCroppingSize = caps.MinOutputSize;
|
||||
caps.MaxCroppingSize = caps.MinOutputSize;
|
||||
caps.CropGranularityX = vih->bmiHeader.biWidth;
|
||||
caps.CropGranularityY = vih->bmiHeader.biHeight;
|
||||
caps.MinBitsPerSecond = vih->dwBitRate;
|
||||
caps.MaxBitsPerSecond = caps.MinBitsPerSecond;
|
||||
|
||||
*ppmt = pmt;
|
||||
|
||||
memcpy(pSCC, &caps, sizeof(caps));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP OutputPin::SetFormat(AM_MEDIA_TYPE *pmt)
|
||||
{
|
||||
PrintFunc(L"OutputPin::SetFormat");
|
||||
|
||||
if (pmt == nullptr)
|
||||
return VFW_E_INVALIDMEDIATYPE;
|
||||
|
||||
mt = pmt;
|
||||
|
||||
GetMediaTypeVFormat(mt, curVFormat);
|
||||
|
||||
VIDEOINFOHEADER *vih = reinterpret_cast<decltype(vih)>(mt->pbFormat);
|
||||
curCX = vih->bmiHeader.biWidth;
|
||||
curCY = vih->bmiHeader.biHeight;
|
||||
curInterval = vih->AvgTimePerFrame;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP OutputPin::Set(REFGUID, DWORD, void *, DWORD, void *, DWORD)
|
||||
{
|
||||
PrintFunc(L"OutputPin::Set");
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
STDMETHODIMP OutputPin::Get(REFGUID guidPropSet, DWORD dwPropID, void *, DWORD,
|
||||
void *pPropData, DWORD cbPropData,
|
||||
DWORD *pcbReturned)
|
||||
{
|
||||
PrintFunc(L"OutputPin::Get");
|
||||
|
||||
if (guidPropSet != AMPROPSETID_Pin)
|
||||
return E_PROP_SET_UNSUPPORTED;
|
||||
if (dwPropID != AMPROPERTY_PIN_CATEGORY)
|
||||
return E_PROP_ID_UNSUPPORTED;
|
||||
if (pPropData == NULL && pcbReturned == NULL)
|
||||
return E_POINTER;
|
||||
|
||||
if (pcbReturned)
|
||||
*pcbReturned = sizeof(GUID);
|
||||
if (pPropData == NULL)
|
||||
return S_OK;
|
||||
if (cbPropData < sizeof(GUID))
|
||||
return E_UNEXPECTED;
|
||||
|
||||
*(GUID *)pPropData = PIN_CATEGORY_CAPTURE;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP OutputPin::QuerySupported(REFGUID guidPropSet, DWORD dwPropID,
|
||||
DWORD *pTypeSupport)
|
||||
{
|
||||
PrintFunc(L"OutputPin::QuerySupported");
|
||||
|
||||
if (guidPropSet != AMPROPSETID_Pin)
|
||||
return E_PROP_SET_UNSUPPORTED;
|
||||
if (dwPropID != AMPROPERTY_PIN_CATEGORY)
|
||||
return E_PROP_ID_UNSUPPORTED;
|
||||
if (pTypeSupport)
|
||||
*pTypeSupport = KSPROPERTY_SUPPORT_GET;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
bool OutputPin::AllocateBuffers(IPin *target, bool connecting)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
ComQIPtr<IMemInputPin> memInput(target);
|
||||
if (!memInput)
|
||||
return false;
|
||||
|
||||
if (!!allocator) {
|
||||
allocator->Decommit();
|
||||
}
|
||||
|
||||
hr = memInput->GetAllocator(&allocator);
|
||||
if (hr == VFW_E_NO_ALLOCATOR)
|
||||
hr = CoCreateInstance(CLSID_MemoryAllocator, NULL,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
__uuidof(IMemAllocator),
|
||||
(void **)&allocator);
|
||||
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
VIDEOINFOHEADER *vih = reinterpret_cast<decltype(vih)>(mt->pbFormat);
|
||||
|
||||
int cx = vih->bmiHeader.biWidth;
|
||||
int cy = vih->bmiHeader.biHeight;
|
||||
|
||||
WORD bits = VFormatBits(curVFormat);
|
||||
bufSize = cx * cy * bits / 8;
|
||||
|
||||
ALLOCATOR_PROPERTIES props;
|
||||
|
||||
hr = memInput->GetAllocatorRequirements(&props);
|
||||
if (hr == E_NOTIMPL) {
|
||||
props.cBuffers = 4;
|
||||
props.cbAlign = 32;
|
||||
props.cbPrefix = 0;
|
||||
|
||||
} else if (FAILED(hr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
props.cbBuffer = (long)bufSize;
|
||||
|
||||
ALLOCATOR_PROPERTIES actual;
|
||||
hr = allocator->SetProperties(&props, &actual);
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
if (!connecting && FAILED(allocator->Commit())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memInput->NotifyAllocator(allocator, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
static MediaType CreateMediaType(VideoFormat format, int cx, int cy,
|
||||
long long interval)
|
||||
{
|
||||
MediaType mt;
|
||||
|
||||
WORD bits = VFormatBits(format);
|
||||
DWORD size = cx * cy * bits / 8;
|
||||
uint64_t rate =
|
||||
(uint64_t)size * 10000000ULL / (uint64_t)interval * 8ULL;
|
||||
|
||||
VIDEOINFOHEADER *vih = mt.AllocFormat<VIDEOINFOHEADER>();
|
||||
vih->bmiHeader.biSize = sizeof(vih->bmiHeader);
|
||||
vih->bmiHeader.biWidth = cx;
|
||||
vih->bmiHeader.biHeight = cy;
|
||||
vih->bmiHeader.biPlanes = VFormatPlanes(format);
|
||||
vih->bmiHeader.biBitCount = bits;
|
||||
vih->bmiHeader.biSizeImage = size;
|
||||
vih->bmiHeader.biCompression = VFormatToFourCC(format);
|
||||
vih->rcSource.right = cx;
|
||||
vih->rcSource.bottom = cy;
|
||||
vih->rcTarget = vih->rcSource;
|
||||
vih->dwBitRate = (DWORD)rate;
|
||||
vih->AvgTimePerFrame = interval;
|
||||
|
||||
mt->majortype = MEDIATYPE_Video;
|
||||
mt->subtype = VFormatToSubType(format);
|
||||
mt->formattype = FORMAT_VideoInfo;
|
||||
mt->bFixedSizeSamples = true;
|
||||
mt->lSampleSize = size;
|
||||
|
||||
return mt;
|
||||
}
|
||||
|
||||
void OutputPin::AddVideoFormat(VideoFormat format, int cx, int cy,
|
||||
long long interval)
|
||||
{
|
||||
MediaType newMT = CreateMediaType(format, cx, cy, interval);
|
||||
mtList.push_back(newMT);
|
||||
}
|
||||
|
||||
bool OutputPin::SetVideoFormat(VideoFormat format, int cx, int cy,
|
||||
long long interval)
|
||||
{
|
||||
mt = CreateMediaType(format, cx, cy, interval);
|
||||
|
||||
if (curCX != cx || curCY != cy || curInterval != interval ||
|
||||
curVFormat != format) {
|
||||
curVFormat = format;
|
||||
curCX = cx;
|
||||
curCY = cy;
|
||||
curInterval = interval;
|
||||
|
||||
if (!!connectedPin) {
|
||||
setSampleMediaType = true;
|
||||
return ReallocateBuffers();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OutputPin::LockSampleData(unsigned char **ptr)
|
||||
{
|
||||
if (!connectedPin)
|
||||
return false;
|
||||
|
||||
ComQIPtr<IMemInputPin> memInput(connectedPin);
|
||||
HRESULT hr;
|
||||
|
||||
if (!memInput || !allocator)
|
||||
return false;
|
||||
|
||||
hr = allocator->GetBuffer(&sample, nullptr, nullptr, 0);
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
if (FAILED(sample->SetActualDataLength((long)bufSize)))
|
||||
return false;
|
||||
if (FAILED(sample->SetDiscontinuity(false)))
|
||||
return false;
|
||||
if (FAILED(sample->SetPreroll(false)))
|
||||
return false;
|
||||
if (FAILED(sample->SetSyncPoint(true)))
|
||||
return false;
|
||||
if (FAILED(sample->GetPointer(ptr)))
|
||||
return false;
|
||||
|
||||
if (setSampleMediaType) {
|
||||
sample->SetMediaType(mt);
|
||||
setSampleMediaType = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void OutputPin::Send(unsigned char *data[DSHOW_MAX_PLANES],
|
||||
size_t linesize[DSHOW_MAX_PLANES],
|
||||
long long timestampStart, long long timestampEnd)
|
||||
{
|
||||
BYTE *ptr;
|
||||
if (!LockSampleData(&ptr))
|
||||
return;
|
||||
|
||||
size_t total = 0;
|
||||
for (size_t i = 0; i < DSHOW_MAX_PLANES; i++) {
|
||||
if (!linesize[i])
|
||||
break;
|
||||
|
||||
memcpy(ptr + total, data[i], linesize[i]);
|
||||
total += linesize[i];
|
||||
}
|
||||
|
||||
UnlockSampleData(timestampStart, timestampEnd);
|
||||
}
|
||||
|
||||
void OutputPin::UnlockSampleData(long long timestampStart,
|
||||
long long timestampEnd)
|
||||
{
|
||||
if (!connectedPin)
|
||||
return;
|
||||
|
||||
ComQIPtr<IMemInputPin> memInput(connectedPin);
|
||||
REFERENCE_TIME startTime = timestampStart;
|
||||
REFERENCE_TIME endTime = timestampEnd;
|
||||
|
||||
sample->SetMediaTime(&startTime, &endTime);
|
||||
sample->SetTime(&startTime, &endTime);
|
||||
|
||||
memInput->Receive(sample);
|
||||
|
||||
sample.Clear();
|
||||
}
|
||||
|
||||
void OutputPin::Stop()
|
||||
{
|
||||
if (!!connectedPin) {
|
||||
connectedPin->BeginFlush();
|
||||
connectedPin->EndFlush();
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
class SourceMiscFlags : public IAMFilterMiscFlags {
|
||||
volatile long refCount = 0;
|
||||
|
||||
public:
|
||||
inline SourceMiscFlags() {}
|
||||
virtual ~SourceMiscFlags() {}
|
||||
|
||||
STDMETHODIMP QueryInterface(REFIID riid, void **ppv)
|
||||
{
|
||||
if (riid == IID_IUnknown) {
|
||||
AddRef();
|
||||
*ppv = this;
|
||||
} else {
|
||||
*ppv = nullptr;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
STDMETHODIMP_(ULONG) AddRef()
|
||||
{
|
||||
return InterlockedIncrement(&refCount);
|
||||
}
|
||||
|
||||
STDMETHODIMP_(ULONG) Release()
|
||||
{
|
||||
if (!InterlockedDecrement(&refCount)) {
|
||||
delete this;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return refCount;
|
||||
}
|
||||
|
||||
STDMETHODIMP_(ULONG) GetMiscFlags()
|
||||
{
|
||||
return AM_FILTER_MISC_FLAGS_IS_SOURCE;
|
||||
}
|
||||
};
|
||||
|
||||
OutputFilter::OutputFilter()
|
||||
: refCount(0),
|
||||
state(State_Stopped),
|
||||
graph(nullptr),
|
||||
pin(new OutputPin(this)),
|
||||
misc(new SourceMiscFlags)
|
||||
{
|
||||
}
|
||||
|
||||
OutputFilter::OutputFilter(VideoFormat format, int cx, int cy,
|
||||
long long interval)
|
||||
: refCount(0),
|
||||
state(State_Stopped),
|
||||
graph(nullptr),
|
||||
pin(new OutputPin(this, format, cx, cy, interval)),
|
||||
misc(new SourceMiscFlags)
|
||||
{
|
||||
}
|
||||
|
||||
OutputFilter::~OutputFilter() {}
|
||||
|
||||
// IUnknown methods
|
||||
STDMETHODIMP OutputFilter::QueryInterface(REFIID riid, void **ppv)
|
||||
{
|
||||
if (riid == IID_IUnknown) {
|
||||
AddRef();
|
||||
*ppv = this;
|
||||
} else if (riid == IID_IPersist) {
|
||||
AddRef();
|
||||
*ppv = (IPersist *)this;
|
||||
} else if (riid == IID_IMediaFilter) {
|
||||
AddRef();
|
||||
*ppv = (IMediaFilter *)this;
|
||||
} else if (riid == IID_IBaseFilter) {
|
||||
AddRef();
|
||||
*ppv = (IBaseFilter *)this;
|
||||
} else if (riid == IID_IAMFilterMiscFlags) {
|
||||
misc.CopyTo((IAMFilterMiscFlags **)ppv);
|
||||
} else {
|
||||
*ppv = nullptr;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
STDMETHODIMP_(ULONG) OutputFilter::AddRef()
|
||||
{
|
||||
return InterlockedIncrement(&refCount);
|
||||
}
|
||||
|
||||
STDMETHODIMP_(ULONG) OutputFilter::Release()
|
||||
{
|
||||
if (!InterlockedDecrement(&refCount)) {
|
||||
delete this;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return refCount;
|
||||
}
|
||||
|
||||
// IPersist method
|
||||
STDMETHODIMP OutputFilter::GetClassID(CLSID *pClsID)
|
||||
{
|
||||
DSHOW_UNUSED(pClsID);
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
// IMediaFilter methods
|
||||
STDMETHODIMP OutputFilter::GetState(DWORD dwMSecs, FILTER_STATE *State)
|
||||
{
|
||||
PrintFunc(L"OutputFilter::GetState");
|
||||
|
||||
*State = state;
|
||||
|
||||
DSHOW_UNUSED(dwMSecs);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP OutputFilter::SetSyncSource(IReferenceClock *pClock)
|
||||
{
|
||||
clock = pClock;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP OutputFilter::GetSyncSource(IReferenceClock **pClock)
|
||||
{
|
||||
*pClock = clock.Get();
|
||||
if (*pClock) {
|
||||
(*pClock)->AddRef();
|
||||
}
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
STDMETHODIMP OutputFilter::Stop()
|
||||
{
|
||||
PrintFunc(L"OutputFilter::Stop");
|
||||
|
||||
if (state != State_Stopped) {
|
||||
pin->Stop();
|
||||
}
|
||||
|
||||
state = State_Stopped;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP OutputFilter::Pause()
|
||||
{
|
||||
PrintFunc(L"OutputFilter::Pause");
|
||||
|
||||
OutputPin *pin = GetPin();
|
||||
if (!!pin->allocator && state == State_Stopped) {
|
||||
pin->allocator->Commit();
|
||||
}
|
||||
|
||||
state = State_Paused;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP OutputFilter::Run(REFERENCE_TIME tStart)
|
||||
{
|
||||
PrintFunc(L"OutputFilter::Run");
|
||||
|
||||
state = State_Running;
|
||||
|
||||
DSHOW_UNUSED(tStart);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// IBaseFilter methods
|
||||
STDMETHODIMP OutputFilter::EnumPins(IEnumPins **ppEnum)
|
||||
{
|
||||
PrintFunc(L"OutputFilter::EnumPins");
|
||||
|
||||
*ppEnum = new OutputEnumPins(this, nullptr);
|
||||
return (*ppEnum == nullptr) ? E_OUTOFMEMORY : NOERROR;
|
||||
}
|
||||
|
||||
STDMETHODIMP OutputFilter::FindPin(LPCWSTR Id, IPin **ppPin)
|
||||
{
|
||||
PrintFunc(L"OutputFilter::FindPin");
|
||||
|
||||
if (Id == nullptr || ppPin == nullptr)
|
||||
return E_POINTER;
|
||||
|
||||
if (lstrcmpW(Id, OUTPUT_PIN_NAME) == 0) {
|
||||
*ppPin = pin;
|
||||
pin->AddRef();
|
||||
return S_OK;
|
||||
} else {
|
||||
*ppPin = nullptr;
|
||||
return VFW_E_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
STDMETHODIMP OutputFilter::QueryFilterInfo(FILTER_INFO *pInfo)
|
||||
{
|
||||
PrintFunc(L"OutputFilter::QueryFilterInfo");
|
||||
|
||||
StringCbCopyW(pInfo->achName, sizeof(pInfo->achName), FilterName());
|
||||
|
||||
pInfo->pGraph = graph;
|
||||
if (graph) {
|
||||
IFilterGraph *graph_ptr = graph;
|
||||
graph_ptr->AddRef();
|
||||
}
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
STDMETHODIMP OutputFilter::JoinFilterGraph(IFilterGraph *pGraph, LPCWSTR pName)
|
||||
{
|
||||
DSHOW_UNUSED(pName);
|
||||
|
||||
graph = pGraph;
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
STDMETHODIMP OutputFilter::QueryVendorInfo(LPWSTR *pVendorInfo)
|
||||
{
|
||||
DSHOW_UNUSED(pVendorInfo);
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
const wchar_t *OutputFilter::FilterName() const
|
||||
{
|
||||
return FILTER_NAME;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
OutputEnumPins::OutputEnumPins(OutputFilter *filter_, OutputEnumPins *pEnum)
|
||||
: filter(filter_)
|
||||
{
|
||||
curPin = (pEnum != nullptr) ? pEnum->curPin : 0;
|
||||
}
|
||||
|
||||
OutputEnumPins::~OutputEnumPins() {}
|
||||
|
||||
// IUnknown
|
||||
STDMETHODIMP OutputEnumPins::QueryInterface(REFIID riid, void **ppv)
|
||||
{
|
||||
if (riid == IID_IUnknown || riid == IID_IEnumPins) {
|
||||
AddRef();
|
||||
*ppv = (IEnumPins *)this;
|
||||
return NOERROR;
|
||||
} else {
|
||||
*ppv = nullptr;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
}
|
||||
|
||||
STDMETHODIMP_(ULONG) OutputEnumPins::AddRef()
|
||||
{
|
||||
return (ULONG)InterlockedIncrement(&refCount);
|
||||
}
|
||||
|
||||
STDMETHODIMP_(ULONG) OutputEnumPins::Release()
|
||||
{
|
||||
if (!InterlockedDecrement(&refCount)) {
|
||||
delete this;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (ULONG)refCount;
|
||||
}
|
||||
|
||||
// IEnumPins
|
||||
STDMETHODIMP OutputEnumPins::Next(ULONG cPins, IPin **ppPins, ULONG *pcFetched)
|
||||
{
|
||||
UINT nFetched = 0;
|
||||
|
||||
if (curPin == 0 && cPins > 0) {
|
||||
IPin *pPin = filter->GetPin();
|
||||
|
||||
*ppPins = pPin;
|
||||
pPin->AddRef();
|
||||
|
||||
nFetched = 1;
|
||||
curPin++;
|
||||
}
|
||||
|
||||
if (pcFetched)
|
||||
*pcFetched = nFetched;
|
||||
|
||||
return (nFetched == cPins) ? S_OK : S_FALSE;
|
||||
}
|
||||
|
||||
STDMETHODIMP OutputEnumPins::Skip(ULONG cPins)
|
||||
{
|
||||
return ((curPin += cPins) > 1) ? S_FALSE : S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP OutputEnumPins::Reset()
|
||||
{
|
||||
curPin = 0;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP OutputEnumPins::Clone(IEnumPins **ppEnum)
|
||||
{
|
||||
*ppEnum = new OutputEnumPins(filter, this);
|
||||
return (*ppEnum == nullptr) ? E_OUTOFMEMORY : NOERROR;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
OutputEnumMediaTypes::OutputEnumMediaTypes(OutputPin *pin_) : pin(pin_) {}
|
||||
|
||||
OutputEnumMediaTypes::~OutputEnumMediaTypes() {}
|
||||
|
||||
STDMETHODIMP OutputEnumMediaTypes::QueryInterface(REFIID riid, void **ppv)
|
||||
{
|
||||
if (riid == IID_IUnknown || riid == IID_IEnumMediaTypes) {
|
||||
AddRef();
|
||||
*ppv = this;
|
||||
return NOERROR;
|
||||
} else {
|
||||
*ppv = nullptr;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
}
|
||||
|
||||
STDMETHODIMP_(ULONG) OutputEnumMediaTypes::AddRef()
|
||||
{
|
||||
return (ULONG)InterlockedIncrement(&refCount);
|
||||
}
|
||||
|
||||
STDMETHODIMP_(ULONG) OutputEnumMediaTypes::Release()
|
||||
{
|
||||
if (!InterlockedDecrement(&refCount)) {
|
||||
delete this;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (ULONG)refCount;
|
||||
}
|
||||
|
||||
// IEnumMediaTypes
|
||||
STDMETHODIMP OutputEnumMediaTypes::Next(ULONG cMediaTypes,
|
||||
AM_MEDIA_TYPE **ppMediaTypes,
|
||||
ULONG *pcFetched)
|
||||
{
|
||||
PrintFunc(L"OutputEnumMediaTypes::Next");
|
||||
|
||||
UINT total = (UINT)pin->mtList.size();
|
||||
UINT nFetched = 0;
|
||||
|
||||
for (ULONG i = 0; i < cMediaTypes && curMT < total; i++) {
|
||||
*(ppMediaTypes++) = pin->mtList[curMT++].Duplicate();
|
||||
nFetched++;
|
||||
}
|
||||
|
||||
if (pcFetched)
|
||||
*pcFetched = nFetched;
|
||||
|
||||
return (nFetched == cMediaTypes) ? S_OK : S_FALSE;
|
||||
}
|
||||
|
||||
STDMETHODIMP OutputEnumMediaTypes::Skip(ULONG cMediaTypes)
|
||||
{
|
||||
PrintFunc(L"OutputEnumMediaTypes::Skip");
|
||||
|
||||
UINT total = (UINT)pin->mtList.size();
|
||||
return ((curMT += cMediaTypes) > total) ? S_FALSE : S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP OutputEnumMediaTypes::Reset()
|
||||
{
|
||||
PrintFunc(L"OutputEnumMediaTypes::Reset");
|
||||
|
||||
curMT = 0;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP OutputEnumMediaTypes::Clone(IEnumMediaTypes **ppEnum)
|
||||
{
|
||||
*ppEnum = new OutputEnumMediaTypes(pin);
|
||||
return (*ppEnum == nullptr) ? E_OUTOFMEMORY : NOERROR;
|
||||
}
|
||||
|
||||
}; /* namespace DShow */
|
||||
262
obs-studio/deps/libdshowcapture/src/source/output-filter.hpp
Normal file
262
obs-studio/deps/libdshowcapture/src/source/output-filter.hpp
Normal file
@ -0,0 +1,262 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Lain Bailey <lain@obsproject.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "dshow-base.hpp"
|
||||
#include "dshow-media-type.hpp"
|
||||
#include "../dshowcapture.hpp"
|
||||
|
||||
namespace DShow {
|
||||
|
||||
class OutputFilter;
|
||||
|
||||
class OutputPin : public IPin, public IAMStreamConfig, public IKsPropertySet {
|
||||
friend class OutputEnumMediaTypes;
|
||||
friend class OutputFilter;
|
||||
|
||||
volatile long refCount;
|
||||
|
||||
std::vector<MediaType> mtList;
|
||||
|
||||
MediaType mt;
|
||||
VideoFormat curVFormat;
|
||||
long long curInterval = 0;
|
||||
int curCX = 0;
|
||||
int curCY = 0;
|
||||
bool setSampleMediaType = false;
|
||||
|
||||
ComPtr<IPin> connectedPin;
|
||||
OutputFilter *filter;
|
||||
volatile bool flushing = false;
|
||||
ComPtr<IMemAllocator> allocator;
|
||||
ComPtr<IMediaSample> sample;
|
||||
size_t bufSize;
|
||||
|
||||
bool IsValidMediaType(const AM_MEDIA_TYPE *pmt) const;
|
||||
|
||||
bool AllocateBuffers(IPin *target, bool connecting = false);
|
||||
|
||||
public:
|
||||
OutputPin(OutputFilter *filter);
|
||||
OutputPin(OutputFilter *filter, VideoFormat format, int cx, int cy,
|
||||
long long interval);
|
||||
virtual ~OutputPin();
|
||||
|
||||
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
|
||||
STDMETHODIMP_(ULONG) AddRef();
|
||||
STDMETHODIMP_(ULONG) Release();
|
||||
|
||||
// IPin methods
|
||||
STDMETHODIMP Connect(IPin *pReceivePin, const AM_MEDIA_TYPE *pmt);
|
||||
STDMETHODIMP ReceiveConnection(IPin *connector,
|
||||
const AM_MEDIA_TYPE *pmt);
|
||||
STDMETHODIMP Disconnect();
|
||||
STDMETHODIMP ConnectedTo(IPin **pPin);
|
||||
STDMETHODIMP ConnectionMediaType(AM_MEDIA_TYPE *pmt);
|
||||
STDMETHODIMP QueryPinInfo(PIN_INFO *pInfo);
|
||||
STDMETHODIMP QueryDirection(PIN_DIRECTION *pPinDir);
|
||||
STDMETHODIMP QueryId(LPWSTR *lpId);
|
||||
STDMETHODIMP QueryAccept(const AM_MEDIA_TYPE *pmt);
|
||||
STDMETHODIMP EnumMediaTypes(IEnumMediaTypes **ppEnum);
|
||||
STDMETHODIMP QueryInternalConnections(IPin **apPin, ULONG *nPin);
|
||||
STDMETHODIMP EndOfStream();
|
||||
|
||||
STDMETHODIMP BeginFlush();
|
||||
STDMETHODIMP EndFlush();
|
||||
STDMETHODIMP NewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop,
|
||||
double dRate);
|
||||
|
||||
// IAMStreamConfig methods
|
||||
STDMETHODIMP GetFormat(AM_MEDIA_TYPE **ppmt) override;
|
||||
STDMETHODIMP GetNumberOfCapabilities(int *piCount,
|
||||
int *piSize) override;
|
||||
STDMETHODIMP GetStreamCaps(int iIndex, AM_MEDIA_TYPE **ppmt,
|
||||
BYTE *pSCC) override;
|
||||
STDMETHODIMP SetFormat(AM_MEDIA_TYPE *pmt) override;
|
||||
|
||||
// IKsPropertySet methods
|
||||
STDMETHODIMP Set(REFGUID guidPropSet, DWORD dwID, void *pInstanceData,
|
||||
DWORD cbInstanceData, void *pPropData,
|
||||
DWORD cbPropData) override;
|
||||
|
||||
STDMETHODIMP Get(REFGUID guidPropSet, DWORD dwPropID,
|
||||
void *pInstanceData, DWORD cbInstanceData,
|
||||
void *pPropData, DWORD cbPropData,
|
||||
DWORD *pcbReturned) override;
|
||||
|
||||
STDMETHODIMP QuerySupported(REFGUID guidPropSet, DWORD dwPropID,
|
||||
DWORD *pTypeSupport) override;
|
||||
|
||||
// Other methods
|
||||
inline bool ReallocateBuffers()
|
||||
{
|
||||
return !!connectedPin ? AllocateBuffers(connectedPin) : false;
|
||||
}
|
||||
|
||||
inline VideoFormat GetVideoFormat() const { return curVFormat; }
|
||||
inline int GetCX() const { return curCX; }
|
||||
inline int GetCY() const { return curCY; }
|
||||
inline long long GetInterval() const { return curInterval; }
|
||||
|
||||
void AddVideoFormat(VideoFormat format, int cx, int cy,
|
||||
long long interval);
|
||||
bool SetVideoFormat(VideoFormat format, int cx, int cy,
|
||||
long long interval);
|
||||
|
||||
void Send(unsigned char *data[DSHOW_MAX_PLANES],
|
||||
size_t linesize[DSHOW_MAX_PLANES], long long timestampStart,
|
||||
long long timestampEnd);
|
||||
|
||||
bool LockSampleData(unsigned char **ptr);
|
||||
void UnlockSampleData(long long timestampStart, long long timestampEnd);
|
||||
|
||||
void Stop();
|
||||
};
|
||||
|
||||
class OutputFilter : public IBaseFilter {
|
||||
friend class OutputPin;
|
||||
|
||||
volatile long refCount;
|
||||
FILTER_STATE state;
|
||||
IFilterGraph *graph;
|
||||
ComPtr<OutputPin> pin;
|
||||
|
||||
ComPtr<IAMFilterMiscFlags> misc;
|
||||
|
||||
protected:
|
||||
ComPtr<IReferenceClock> clock;
|
||||
|
||||
public:
|
||||
OutputFilter();
|
||||
OutputFilter(VideoFormat format, int cx, int cy, long long interval);
|
||||
virtual ~OutputFilter();
|
||||
|
||||
// IUnknown methods
|
||||
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
|
||||
STDMETHODIMP_(ULONG) AddRef();
|
||||
STDMETHODIMP_(ULONG) Release();
|
||||
|
||||
// IPersist method
|
||||
STDMETHODIMP GetClassID(CLSID *pClsID);
|
||||
|
||||
// IMediaFilter methods
|
||||
STDMETHODIMP GetState(DWORD dwMSecs, FILTER_STATE *State);
|
||||
STDMETHODIMP SetSyncSource(IReferenceClock *pClock);
|
||||
STDMETHODIMP GetSyncSource(IReferenceClock **pClock);
|
||||
STDMETHODIMP Stop();
|
||||
STDMETHODIMP Pause();
|
||||
STDMETHODIMP Run(REFERENCE_TIME tStart);
|
||||
|
||||
// IBaseFilter methods
|
||||
STDMETHODIMP EnumPins(IEnumPins **ppEnum);
|
||||
STDMETHODIMP FindPin(LPCWSTR Id, IPin **ppPin);
|
||||
STDMETHODIMP QueryFilterInfo(FILTER_INFO *pInfo);
|
||||
STDMETHODIMP JoinFilterGraph(IFilterGraph *pGraph, LPCWSTR pName);
|
||||
STDMETHODIMP QueryVendorInfo(LPWSTR *pVendorInfo);
|
||||
|
||||
virtual const wchar_t *FilterName() const;
|
||||
|
||||
inline OutputPin *GetPin() const { return (OutputPin *)pin; }
|
||||
|
||||
inline bool ReallocateBuffers() { return pin->ReallocateBuffers(); }
|
||||
|
||||
inline VideoFormat GetVideoFormat() const
|
||||
{
|
||||
return pin->GetVideoFormat();
|
||||
}
|
||||
|
||||
inline int GetCX() const { return pin->GetCX(); }
|
||||
inline int GetCY() const { return pin->GetCY(); }
|
||||
inline long long GetInterval() const { return pin->GetInterval(); }
|
||||
|
||||
inline void AddVideoFormat(VideoFormat format, int cx, int cy,
|
||||
long long interval)
|
||||
{
|
||||
pin->AddVideoFormat(format, cx, cy, interval);
|
||||
}
|
||||
inline bool SetVideoFormat(VideoFormat format, int cx, int cy,
|
||||
long long interval)
|
||||
{
|
||||
return pin->SetVideoFormat(format, cx, cy, interval);
|
||||
}
|
||||
|
||||
inline void Send(unsigned char *data[DSHOW_MAX_PLANES],
|
||||
size_t linesize[DSHOW_MAX_PLANES],
|
||||
long long timestampStart, long long timestampEnd)
|
||||
{
|
||||
pin->Send(data, linesize, timestampStart, timestampEnd);
|
||||
}
|
||||
|
||||
inline bool LockSampleData(unsigned char **ptr)
|
||||
{
|
||||
return pin->LockSampleData(ptr);
|
||||
}
|
||||
|
||||
inline void UnlockSampleData(long long timestampStart,
|
||||
long long timestampEnd)
|
||||
{
|
||||
pin->UnlockSampleData(timestampStart, timestampEnd);
|
||||
}
|
||||
};
|
||||
|
||||
class OutputEnumPins : public IEnumPins {
|
||||
volatile long refCount = 1;
|
||||
ComPtr<OutputFilter> filter;
|
||||
UINT curPin;
|
||||
|
||||
public:
|
||||
OutputEnumPins(OutputFilter *filter, OutputEnumPins *pEnum);
|
||||
virtual ~OutputEnumPins();
|
||||
|
||||
// IUnknown
|
||||
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
|
||||
STDMETHODIMP_(ULONG) AddRef();
|
||||
STDMETHODIMP_(ULONG) Release();
|
||||
|
||||
// IEnumPins
|
||||
STDMETHODIMP Next(ULONG cPins, IPin **ppPins, ULONG *pcFetched);
|
||||
STDMETHODIMP Skip(ULONG cPins);
|
||||
STDMETHODIMP Reset();
|
||||
STDMETHODIMP Clone(IEnumPins **ppEnum);
|
||||
};
|
||||
|
||||
class OutputEnumMediaTypes : public IEnumMediaTypes {
|
||||
volatile long refCount = 1;
|
||||
ComPtr<OutputPin> pin;
|
||||
UINT curMT = 0;
|
||||
|
||||
public:
|
||||
OutputEnumMediaTypes(OutputPin *pin);
|
||||
virtual ~OutputEnumMediaTypes();
|
||||
|
||||
// IUnknown
|
||||
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
|
||||
STDMETHODIMP_(ULONG) AddRef();
|
||||
STDMETHODIMP_(ULONG) Release();
|
||||
|
||||
// IEnumMediaTypes
|
||||
STDMETHODIMP Next(ULONG cMediaTypes, AM_MEDIA_TYPE **ppMediaTypes,
|
||||
ULONG *pcFetched);
|
||||
STDMETHODIMP Skip(ULONG cMediaTypes);
|
||||
STDMETHODIMP Reset();
|
||||
STDMETHODIMP Clone(IEnumMediaTypes **ppEnum);
|
||||
};
|
||||
|
||||
}; /* namespace DShow */
|
||||
0
obs-studio/deps/libdshowcapture/src/tests/.gitignore
vendored
Normal file
0
obs-studio/deps/libdshowcapture/src/tests/.gitignore
vendored
Normal file
26
obs-studio/deps/libdshowcapture/src/vs/2013/dshowcapture.sln
Normal file
26
obs-studio/deps/libdshowcapture/src/vs/2013/dshowcapture.sln
Normal file
@ -0,0 +1,26 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 11.00
|
||||
# Visual Studio 2010
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dshowcapture", "dshowcapture\dshowcapture.vcxproj", "{FFF52519-38BB-4155-851D-4209EC67ACA7}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Debug|x64 = Debug|x64
|
||||
Release|Win32 = Release|Win32
|
||||
Release|x64 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{FFF52519-38BB-4155-851D-4209EC67ACA7}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{FFF52519-38BB-4155-851D-4209EC67ACA7}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{FFF52519-38BB-4155-851D-4209EC67ACA7}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{FFF52519-38BB-4155-851D-4209EC67ACA7}.Debug|x64.Build.0 = Debug|x64
|
||||
{FFF52519-38BB-4155-851D-4209EC67ACA7}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{FFF52519-38BB-4155-851D-4209EC67ACA7}.Release|Win32.Build.0 = Release|Win32
|
||||
{FFF52519-38BB-4155-851D-4209EC67ACA7}.Release|x64.ActiveCfg = Release|x64
|
||||
{FFF52519-38BB-4155-851D-4209EC67ACA7}.Release|x64.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@ -0,0 +1,179 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{FFF52519-38BB-4155-851D-4209EC67ACA7}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>dshowcapture</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<PlatformToolset>v120</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<PlatformToolset>v120</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<PlatformToolset>v120</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<PlatformToolset>v120</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;DSHOWCAPTURE_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>strmiids.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;DSHOWCAPTURE_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>strmiids.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;DSHOWCAPTURE_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>strmiids.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;DSHOWCAPTURE_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>strmiids.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\..\source\capture-filter.cpp" />
|
||||
<ClCompile Include="..\..\..\source\device.cpp" />
|
||||
<ClCompile Include="..\..\..\source\dshow-base.cpp" />
|
||||
<ClCompile Include="..\..\..\source\dshow-demux.cpp" />
|
||||
<ClCompile Include="..\..\..\source\dshow-encoded-device.cpp" />
|
||||
<ClCompile Include="..\..\..\source\dshow-enum.cpp" />
|
||||
<ClCompile Include="..\..\..\source\dshow-formats.cpp" />
|
||||
<ClCompile Include="..\..\..\source\dshow-media-type.cpp" />
|
||||
<ClCompile Include="..\..\..\source\dshowcapture.cpp" />
|
||||
<ClCompile Include="..\..\..\source\dshowencode.cpp" />
|
||||
<ClCompile Include="..\..\..\source\encoder.cpp" />
|
||||
<ClCompile Include="..\..\..\source\log.cpp" />
|
||||
<ClCompile Include="..\..\..\source\output-filter.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\..\dshowcapture.hpp" />
|
||||
<ClInclude Include="..\..\..\source\capture-filter.hpp" />
|
||||
<ClInclude Include="..\..\..\source\ComPtr.hpp" />
|
||||
<ClInclude Include="..\..\..\source\device.hpp" />
|
||||
<ClInclude Include="..\..\..\source\dshow-base.hpp" />
|
||||
<ClInclude Include="..\..\..\source\dshow-demux.hpp" />
|
||||
<ClInclude Include="..\..\..\source\dshow-device-defs.hpp" />
|
||||
<ClInclude Include="..\..\..\source\dshow-enum.hpp" />
|
||||
<ClInclude Include="..\..\..\source\dshow-formats.hpp" />
|
||||
<ClInclude Include="..\..\..\source\dshow-media-type.hpp" />
|
||||
<ClInclude Include="..\..\..\source\encoder.hpp" />
|
||||
<ClInclude Include="..\..\..\source\IVideoCaptureFilter.h" />
|
||||
<ClInclude Include="..\..\..\source\log.hpp" />
|
||||
<ClInclude Include="..\..\..\source\output-filter.hpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@ -0,0 +1,102 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\..\source\capture-filter.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\device.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\dshow-base.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\dshow-enum.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\dshow-formats.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\dshow-media-type.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\dshowcapture.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\log.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\dshow-demux.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\dshow-encoded-device.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\output-filter.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\dshowencode.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\encoder.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\..\source\capture-filter.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\device.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\dshow-base.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\dshow-enum.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\dshow-formats.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\dshow-media-type.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\log.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\dshowcapture.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\dshow-demux.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\IVideoCaptureFilter.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\dshow-device-defs.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\output-filter.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\encoder.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\ComPtr.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@ -748,34 +748,7 @@ bool OBSApp::InitLocale()
|
||||
}
|
||||
|
||||
if (!userLocale && defaultLang) {
|
||||
for (auto &locale_ : GetPreferredLocales()) {
|
||||
if (locale_ == lang) {
|
||||
return true;
|
||||
}
|
||||
|
||||
stringstream file;
|
||||
file << "locale/" << locale_ << ".ini";
|
||||
|
||||
string path;
|
||||
if (!GetDataFilePath(file.str().c_str(), path)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!text_lookup_add(textLookup, path.c_str())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
blog(LOG_INFO, "Using preferred locale '%s'", locale_.c_str());
|
||||
locale = locale_;
|
||||
|
||||
// set application default locale to the new chosen one
|
||||
if (!locale.empty()) {
|
||||
QLocale::setDefault(QLocale(QString::fromStdString(locale).replace('-', '_')));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Default to English UI; ignore system preferred locales.
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 91 KiB After Width: | Height: | Size: 64 KiB |
@ -1,18 +1,9 @@
|
||||
#include "OBSAbout.hpp"
|
||||
|
||||
#include <widgets/OBSBasic.hpp>
|
||||
#include <utility/RemoteTextThread.hpp>
|
||||
|
||||
#include <qt-wrappers.hpp>
|
||||
|
||||
#include <json11.hpp>
|
||||
|
||||
#include "moc_OBSAbout.cpp"
|
||||
|
||||
using namespace json11;
|
||||
|
||||
extern bool steam;
|
||||
|
||||
OBSAbout::OBSAbout(QWidget *parent) : QDialog(parent), ui(new Ui::OBSAbout)
|
||||
{
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
@ -31,142 +22,6 @@ OBSAbout::OBSAbout(QWidget *parent) : QDialog(parent), ui(new Ui::OBSAbout)
|
||||
|
||||
ui->version->setText(ver + bitness);
|
||||
|
||||
ui->contribute->setText(QTStr("About.Contribute"));
|
||||
|
||||
if (steam) {
|
||||
delete ui->donate;
|
||||
} else {
|
||||
ui->donate->setText(" <a href='https://obsproject.com/contribute'>" + QTStr("About.Donate") +
|
||||
"</a>");
|
||||
ui->donate->setTextInteractionFlags(Qt::TextBrowserInteraction);
|
||||
ui->donate->setOpenExternalLinks(true);
|
||||
}
|
||||
|
||||
ui->getInvolved->setText(" <a href='https://obsproject.com/developer-contributing'>" +
|
||||
QTStr("About.GetInvolved") + "</a>");
|
||||
ui->getInvolved->setTextInteractionFlags(Qt::TextBrowserInteraction);
|
||||
ui->getInvolved->setOpenExternalLinks(true);
|
||||
|
||||
ui->about->setText("<a href='#'>" + QTStr("About") + "</a>");
|
||||
ui->authors->setText("<a href='#'>" + QTStr("About.Authors") + "</a>");
|
||||
ui->license->setText("<a href='#'>" + QTStr("About.License") + "</a>");
|
||||
|
||||
ui->name->setProperty("class", "text-heading");
|
||||
ui->version->setProperty("class", "text-large");
|
||||
ui->about->setProperty("class", "bg-base");
|
||||
ui->authors->setProperty("class", "bg-base");
|
||||
ui->license->setProperty("class", "bg-base");
|
||||
ui->info->setProperty("class", "");
|
||||
|
||||
connect(ui->about, &ClickableLabel::clicked, this, &OBSAbout::ShowAbout);
|
||||
connect(ui->authors, &ClickableLabel::clicked, this, &OBSAbout::ShowAuthors);
|
||||
connect(ui->license, &ClickableLabel::clicked, this, &OBSAbout::ShowLicense);
|
||||
|
||||
QPointer<OBSAbout> about(this);
|
||||
|
||||
OBSBasic *main = OBSBasic::Get();
|
||||
if (main->patronJson.empty() && !main->patronJsonThread) {
|
||||
RemoteTextThread *thread =
|
||||
new RemoteTextThread("https://obsproject.com/patreon/about-box.json", "application/json");
|
||||
QObject::connect(thread, &RemoteTextThread::Result, main, &OBSBasic::UpdatePatronJson);
|
||||
QObject::connect(thread, &RemoteTextThread::Result, this, &OBSAbout::ShowAbout);
|
||||
main->patronJsonThread.reset(thread);
|
||||
thread->start();
|
||||
} else {
|
||||
ShowAbout();
|
||||
}
|
||||
}
|
||||
|
||||
void OBSAbout::ShowAbout()
|
||||
{
|
||||
OBSBasic *main = OBSBasic::Get();
|
||||
|
||||
if (main->patronJson.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string error;
|
||||
Json json = Json::parse(main->patronJson, error);
|
||||
const Json::array &patrons = json.array_items();
|
||||
QString text;
|
||||
|
||||
text += "<h1>Top Patreon contributors:</h1>";
|
||||
text += "<p style=\"font-size:16px;\">";
|
||||
bool first = true;
|
||||
bool top = true;
|
||||
|
||||
for (const Json &patron : patrons) {
|
||||
std::string name = patron["name"].string_value();
|
||||
std::string link = patron["link"].string_value();
|
||||
int amount = patron["amount"].int_value();
|
||||
|
||||
if (top && amount < 5000) {
|
||||
text += "</p>";
|
||||
top = false;
|
||||
} else if (!first) {
|
||||
text += "<br/>";
|
||||
}
|
||||
|
||||
if (!link.empty()) {
|
||||
text += "<a href=\"";
|
||||
text += QT_UTF8(link.c_str()).toHtmlEscaped();
|
||||
text += "\">";
|
||||
}
|
||||
text += QT_UTF8(name.c_str()).toHtmlEscaped();
|
||||
if (!link.empty()) {
|
||||
text += "</a>";
|
||||
}
|
||||
|
||||
if (first) {
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
ui->textBrowser->setHtml(text);
|
||||
}
|
||||
|
||||
void OBSAbout::ShowAuthors()
|
||||
{
|
||||
std::string path;
|
||||
QString error = QTStr("About.Error").arg("https://github.com/obsproject/obs-studio/blob/master/AUTHORS");
|
||||
|
||||
#ifdef __APPLE__
|
||||
if (!GetDataFilePath("AUTHORS", path)) {
|
||||
#else
|
||||
if (!GetDataFilePath("authors/AUTHORS", path)) {
|
||||
#endif
|
||||
ui->textBrowser->setPlainText(error);
|
||||
return;
|
||||
}
|
||||
|
||||
ui->textBrowser->setPlainText(QString::fromStdString(path));
|
||||
|
||||
BPtr<char> text = os_quick_read_utf8_file(path.c_str());
|
||||
|
||||
if (!text || !*text) {
|
||||
ui->textBrowser->setPlainText(error);
|
||||
return;
|
||||
}
|
||||
|
||||
ui->textBrowser->setPlainText(QT_UTF8(text));
|
||||
}
|
||||
|
||||
void OBSAbout::ShowLicense()
|
||||
{
|
||||
std::string path;
|
||||
QString error = QTStr("About.Error").arg("https://github.com/obsproject/obs-studio/blob/master/COPYING");
|
||||
|
||||
if (!GetDataFilePath("license/gplv2.txt", path)) {
|
||||
ui->textBrowser->setPlainText(error);
|
||||
return;
|
||||
}
|
||||
|
||||
BPtr<char> text = os_quick_read_utf8_file(path.c_str());
|
||||
|
||||
if (!text || !*text) {
|
||||
ui->textBrowser->setPlainText(error);
|
||||
return;
|
||||
}
|
||||
|
||||
ui->textBrowser->setPlainText(QT_UTF8(text));
|
||||
}
|
||||
|
||||
@ -11,9 +11,4 @@ public:
|
||||
explicit OBSAbout(QWidget *parent = 0);
|
||||
|
||||
std::unique_ptr<Ui::OBSAbout> ui;
|
||||
|
||||
private slots:
|
||||
void ShowAbout();
|
||||
void ShowAuthors();
|
||||
void ShowLicense();
|
||||
};
|
||||
|
||||
@ -6,246 +6,90 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>840</width>
|
||||
<height>519</height>
|
||||
<width>520</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>About</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="spacing">
|
||||
<number>30</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
<number>30</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
<number>30</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
<number>30</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
<number>30</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<widget class="QLabel" name="icon">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>200</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>200</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap>:res/images/obs.png</pixmap>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>30</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>30</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>6</number>
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="icon">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>256</width>
|
||||
<height>256</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>256</width>
|
||||
<height>256</height>
|
||||
</size>
|
||||
</property>
|
||||
<widget class="QLabel" name="name">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap>:res/images/obs.png</pixmap>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
<string notr="true">OBS Studio</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
<widget class="QLabel" name="version">
|
||||
<property name="text">
|
||||
<string notr="true">Version</string>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="name">
|
||||
<property name="text">
|
||||
<string notr="true">OBS Studio</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="version">
|
||||
<property name="text">
|
||||
<string notr="true">Version</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Policy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="info">
|
||||
<property name="text">
|
||||
<string>About.Info</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Policy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="contribute">
|
||||
<property name="text">
|
||||
<string notr="true">Contribute</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="donate">
|
||||
<property name="text">
|
||||
<string notr="true">Donate</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="getInvolved">
|
||||
<property name="text">
|
||||
<string notr="true">Get Involved</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Policy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTextBrowser" name="textBrowser">
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="widget">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>60</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="ClickableLabel" name="about">
|
||||
<property name="text">
|
||||
<string>About</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ClickableLabel" name="authors">
|
||||
<property name="text">
|
||||
<string>Authors</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ClickableLabel" name="license">
|
||||
<property name="text">
|
||||
<string>License</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>ClickableLabel</class>
|
||||
<extends>QLabel</extends>
|
||||
<header>components/ClickableLabel.hpp</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 39 KiB |
@ -1654,6 +1654,8 @@ void OBSBasicSettings::LoadResolutionLists()
|
||||
addRes(as_width, as_height);
|
||||
}
|
||||
|
||||
addRes(3840, 2160);
|
||||
addRes(2560, 1440);
|
||||
addRes(1920, 1080);
|
||||
addRes(1280, 720);
|
||||
|
||||
|
||||
@ -622,16 +622,6 @@ bool OBSBasic::InitBasicConfigDefaults()
|
||||
cx *= devicePixelRatioF();
|
||||
cy *= devicePixelRatioF();
|
||||
|
||||
bool oldResolutionDefaults = config_get_bool(App()->GetUserConfig(), "General", "Pre19Defaults");
|
||||
|
||||
/* use 1920x1080 for new default base res if main monitor is above
|
||||
* 1920x1080, but don't apply for people from older builds -- only to
|
||||
* new users */
|
||||
if (!oldResolutionDefaults && (cx * cy) > (1920 * 1080)) {
|
||||
cx = 1920;
|
||||
cy = 1080;
|
||||
}
|
||||
|
||||
bool changed = false;
|
||||
|
||||
/* ----------------------------------------------------- */
|
||||
|
||||
@ -467,14 +467,14 @@ struct Result {
|
||||
|
||||
static void CalcBaseRes(int &baseCX, int &baseCY)
|
||||
{
|
||||
const int maxBaseArea = 1920 * 1200;
|
||||
const int clipResArea = 1920 * 1080;
|
||||
const int maxBaseArea = 3840 * 2160;
|
||||
const int clipResArea = 3840 * 2160;
|
||||
|
||||
/* if base resolution unusually high, recalculate to a more reasonable
|
||||
* value to start the downscaling at, based upon 1920x1080's area.
|
||||
* value to start the downscaling at, based upon 3840x2160's area.
|
||||
*
|
||||
* for 16:9 resolutions this will always change the starting value to
|
||||
* 1920x1080 */
|
||||
* 3840x2160 */
|
||||
if ((baseCX * baseCY) > maxBaseArea) {
|
||||
long double xyAspect = (long double)baseCX / (long double)baseCY;
|
||||
baseCY = (int)sqrt((long double)clipResArea / xyAspect);
|
||||
@ -559,15 +559,15 @@ bool AutoConfigTestPage::TestSoftwareEncoding()
|
||||
int maxDataRate;
|
||||
if (lcores > 8 || pcores > 4) {
|
||||
/* superb */
|
||||
maxDataRate = 1920 * 1200 * 60 + 1000;
|
||||
maxDataRate = 3840 * 2160 * 60 + 1000;
|
||||
|
||||
} else if (lcores > 4 && pcores == 4) {
|
||||
/* great */
|
||||
maxDataRate = 1920 * 1080 * 60 + 1000;
|
||||
maxDataRate = 2560 * 1440 * 60 + 1000;
|
||||
|
||||
} else if (pcores == 4) {
|
||||
/* okay */
|
||||
maxDataRate = 1920 * 1080 * 30 + 1000;
|
||||
maxDataRate = 1920 * 1080 * 60 + 1000;
|
||||
|
||||
} else {
|
||||
/* toaster */
|
||||
@ -779,7 +779,7 @@ void AutoConfigTestPage::FindIdealHardwareResolution()
|
||||
int pcores = os_get_physical_cores();
|
||||
int maxDataRate;
|
||||
if (pcores >= 4) {
|
||||
maxDataRate = 1920 * 1200 * 60 + 1000;
|
||||
maxDataRate = 3840 * 2160 * 60 + 1000;
|
||||
} else {
|
||||
maxDataRate = 1280 * 720 * 30 + 1000;
|
||||
}
|
||||
|
||||
@ -76,6 +76,8 @@ AutoConfigVideoPage::AutoConfigVideoPage(QWidget *parent) : QWizardPage(parent),
|
||||
ui->canvasRes->addItem(str, encRes);
|
||||
};
|
||||
|
||||
addRes(3840, 2160);
|
||||
addRes(2560, 1440);
|
||||
addRes(1920, 1080);
|
||||
addRes(1280, 720);
|
||||
|
||||
|
||||
@ -84,6 +84,7 @@ add_obs_plugin(
|
||||
PLATFORMS WINDOWS LINUX
|
||||
ARCHITECTURES x64 x86_64
|
||||
)
|
||||
add_obs_plugin(obs-shaderfilter)
|
||||
add_obs_plugin(obs-text PLATFORMS WINDOWS)
|
||||
add_obs_plugin(obs-transitions)
|
||||
add_obs_plugin(
|
||||
|
||||
209
obs-studio/plugins/obs-browser/.clang-format
Normal file
209
obs-studio/plugins/obs-browser/.clang-format
Normal file
@ -0,0 +1,209 @@
|
||||
# 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
|
||||
46
obs-studio/plugins/obs-browser/.cmake-format.json
Normal file
46
obs-studio/plugins/obs-browser/.cmake-format.json
Normal file
@ -0,0 +1,46 @@
|
||||
{
|
||||
"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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
82
obs-studio/plugins/obs-browser/.github/actions/check-changes/action.yaml
vendored
Normal file
82
obs-studio/plugins/obs-browser/.github/actions/check-changes/action.yaml
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
name: 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
|
||||
60
obs-studio/plugins/obs-browser/.github/actions/run-clang-format/action.yaml
vendored
Normal file
60
obs-studio/plugins/obs-browser/.github/actions/run-clang-format/action.yaml
vendored
Normal file
@ -0,0 +1,60 @@
|
||||
name: 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::
|
||||
15
obs-studio/plugins/obs-browser/.github/workflows/check-format.yaml
vendored
Normal file
15
obs-studio/plugins/obs-browser/.github/workflows/check-format.yaml
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
name: 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
|
||||
20
obs-studio/plugins/obs-browser/.github/workflows/pr-pull.yaml
vendored
Normal file
20
obs-studio/plugins/obs-browser/.github/workflows/pr-pull.yaml
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
name: 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
|
||||
20
obs-studio/plugins/obs-browser/.github/workflows/push.yaml
vendored
Normal file
20
obs-studio/plugins/obs-browser/.github/workflows/push.yaml
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
name: 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
|
||||
85
obs-studio/plugins/obs-browser/.gitignore
vendored
Normal file
85
obs-studio/plugins/obs-browser/.gitignore
vendored
Normal file
@ -0,0 +1,85 @@
|
||||
#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
|
||||
62
obs-studio/plugins/obs-browser/CMakeLists.txt
Normal file
62
obs-studio/plugins/obs-browser/CMakeLists.txt
Normal file
@ -0,0 +1,62 @@
|
||||
cmake_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-dummy-client.cpp
|
||||
browser-dummy-client.hpp
|
||||
browser-scheme.cpp
|
||||
browser-scheme.hpp
|
||||
browser-version.h
|
||||
cef-headers.hpp
|
||||
deps/base64/base64.cpp
|
||||
deps/base64/base64.hpp
|
||||
deps/ip-string.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_20)
|
||||
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 "")
|
||||
340
obs-studio/plugins/obs-browser/COPYING
Normal file
340
obs-studio/plugins/obs-browser/COPYING
Normal file
@ -0,0 +1,340 @@
|
||||
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.
|
||||
377
obs-studio/plugins/obs-browser/README.md
Normal file
377
obs-studio/plugins/obs-browser/README.md
Normal file
@ -0,0 +1,377 @@
|
||||
# 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.
|
||||
498
obs-studio/plugins/obs-browser/browser-app.cpp
Normal file
498
obs-studio/plugins/obs-browser/browser-app.cpp
Normal file
@ -0,0 +1,498 @@
|
||||
/******************************************************************************
|
||||
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
|
||||
|
||||
#ifdef _WIN32
|
||||
constexpr bool kIsPlatformWindows = true;
|
||||
#else
|
||||
constexpr bool kIsPlatformWindows = false;
|
||||
#endif
|
||||
|
||||
CefRefPtr<CefRenderProcessHandler> BrowserApp::GetRenderProcessHandler()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
CefRefPtr<CefBrowserProcessHandler> BrowserApp::GetBrowserProcessHandler()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
CefRefPtr<CefClient> BrowserApp::GetDefaultClient()
|
||||
{
|
||||
return GetDummy();
|
||||
}
|
||||
|
||||
void BrowserApp::OnRegisterCustomSchemes(CefRawPtr<CefSchemeRegistrar> registrar)
|
||||
{
|
||||
registrar->AddCustomScheme("http", CEF_SCHEME_OPTION_STANDARD | CEF_SCHEME_OPTION_CORS_ENABLED);
|
||||
}
|
||||
|
||||
void BrowserApp::OnContextInitialized()
|
||||
{
|
||||
// Without a default client, CefBrowser is unmanaged, allowing full-blown Chromium windows outside of our control.
|
||||
// We don't actually want those, so define a dummy client which will automatically close any such windows.
|
||||
dummy = new BrowserDummyClient();
|
||||
|
||||
CefRefPtr<CefRequestContext> requestContext = CefRequestContext::GetGlobalContext();
|
||||
CefString errorMessage;
|
||||
CefRefPtr<CefValue> optionValue = CefValue::Create();
|
||||
|
||||
constexpr std::array<std::string_view, 20> kBrowserFeaturesToDisable{
|
||||
"autofill.credit_card_enabled",
|
||||
"autofill.enabled",
|
||||
"autofill.iban_enabled",
|
||||
"autofill.payment_card_benefits",
|
||||
"autofill.payment_cvc_storage",
|
||||
"autofill.profile_enabled",
|
||||
"autologin.enabled",
|
||||
"browser_labs_enabled",
|
||||
"credentials_enable_autosignin",
|
||||
"credentials_enable_service",
|
||||
"payments.can_make_payment_enabled",
|
||||
"printing.enabled",
|
||||
"search.suggest_enabled",
|
||||
"shopping_list_enabled",
|
||||
"side_panel.google_search_side_panel_enabled",
|
||||
"side_search.enabled",
|
||||
"signin.allowed",
|
||||
"signin.allowed_on_next_startup",
|
||||
"translate",
|
||||
"url_keyed_anonymized_data_collection.enabled"};
|
||||
|
||||
constexpr std::array<std::string_view, 2> kBrowserFeaturesToEnable{"extensions.block_external_extensions",
|
||||
"extensions.disabled"};
|
||||
|
||||
optionValue->SetBool(false);
|
||||
for (std::string_view feature : kBrowserFeaturesToDisable) {
|
||||
requestContext->SetPreference(feature.data(), optionValue.get(), errorMessage);
|
||||
}
|
||||
|
||||
optionValue->SetBool(true);
|
||||
for (std::string_view feature : kBrowserFeaturesToEnable) {
|
||||
requestContext->SetPreference(feature.data(), optionValue.get(), errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
// See https://github.com/chromiumembedded/cef/issues/3966 for 'StorageNotificationService' requirement.
|
||||
constexpr std::string_view kDefaultDisabledFeatures{
|
||||
"CalculateNativeWinOcclusion,GlobalShortcutsPortal,HardwareMediaKeyHandling,LiveCaption,"
|
||||
"LocalNetworkAccessChecksWebSockets,MediaRouter,StorageNotificationService,WebBluetooth,"
|
||||
"EnableWindowsGamingInputDataFetcher"};
|
||||
|
||||
std::string disableFeatures{};
|
||||
|
||||
if constexpr (kIsPlatformWindows) {
|
||||
disableFeatures.append(kDefaultDisabledFeatures);
|
||||
} else {
|
||||
constexpr std::string_view kWindowsFeature{",EnableWindowsGamingInputDataFetcher"};
|
||||
constexpr size_t kNonWindowsLength = kDefaultDisabledFeatures.size() - kWindowsFeature.size();
|
||||
disableFeatures.append(kDefaultDisabledFeatures.substr(0, kNonWindowsLength));
|
||||
}
|
||||
|
||||
if (command_line->HasSwitch("disable-features")) {
|
||||
// Don't override existing, as this can break OSR
|
||||
disableFeatures.append(",");
|
||||
disableFeatures.append(command_line->GetSwitchValue("disable-features"));
|
||||
}
|
||||
|
||||
if (command_line->HasSwitch("disable-blink-features")) {
|
||||
std::string disableBlinkFeatures = command_line->GetSwitchValue("disable-blink-features");
|
||||
disableBlinkFeatures += ",DocumentPictureInPictureAPI";
|
||||
command_line->AppendSwitchWithValue("disable-blink-features", disableBlinkFeatures);
|
||||
} else {
|
||||
command_line->AppendSwitchWithValue("disable-blink-features", "DocumentPictureInPictureAPI");
|
||||
}
|
||||
|
||||
command_line->AppendSwitchWithValue("autoplay-policy", "no-user-gesture-required");
|
||||
command_line->AppendSwitch("disable-extensions");
|
||||
command_line->AppendSwitchWithValue("disable-features", disableFeatures);
|
||||
command_line->AppendSwitch("hide-crash-restore-bubble");
|
||||
#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
|
||||
117
obs-studio/plugins/obs-browser/browser-app.hpp
Normal file
117
obs-studio/plugins/obs-browser/browser-app.hpp
Normal file
@ -0,0 +1,117 @@
|
||||
/******************************************************************************
|
||||
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"
|
||||
#include "browser-dummy-client.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 CefRefPtr<CefClient> GetDefaultClient() override;
|
||||
virtual void OnContextInitialized() 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
|
||||
|
||||
CefRefPtr<BrowserDummyClient> dummy = nullptr;
|
||||
|
||||
BrowserDummyClient *GetDummy() const { return dummy.get(); };
|
||||
|
||||
IMPLEMENT_REFCOUNTING(BrowserApp);
|
||||
};
|
||||
666
obs-studio/plugins/obs-browser/browser-client.cpp
Normal file
666
obs-studio/plugins/obs-browser/browser-client.cpp
Normal file
@ -0,0 +1,666 @@
|
||||
/******************************************************************************
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void BrowserClient::OnLoadError(CefRefPtr<CefBrowser>, [[maybe_unused]] CefRefPtr<CefFrame> frame,
|
||||
CefLoadHandler::ErrorCode, const CefString &, const CefString &)
|
||||
{
|
||||
#if CHROME_VERSION_BUILD > 6533
|
||||
// CEF doesn't currently provide a way to properly disable/override the default Chrome error page.
|
||||
// https://github.com/obsproject/obs-studio/issues/13499
|
||||
// FIXME: https://github.com/chromiumembedded/cef/issues/3852
|
||||
frame->LoadURL("about:blank");
|
||||
#endif
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
154
obs-studio/plugins/obs-browser/browser-client.hpp
Normal file
154
obs-studio/plugins/obs-browser/browser-client.hpp
Normal file
@ -0,0 +1,154 @@
|
||||
/******************************************************************************
|
||||
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;
|
||||
|
||||
virtual void OnLoadError(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame,
|
||||
CefLoadHandler::ErrorCode errorCode, const CefString &errorText,
|
||||
const CefString &failedUrl) override;
|
||||
|
||||
IMPLEMENT_REFCOUNTING(BrowserClient);
|
||||
};
|
||||
21
obs-studio/plugins/obs-browser/browser-config.h.in
Normal file
21
obs-studio/plugins/obs-browser/browser-config.h.in
Normal file
@ -0,0 +1,21 @@
|
||||
#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
|
||||
87
obs-studio/plugins/obs-browser/browser-dummy-client.cpp
Normal file
87
obs-studio/plugins/obs-browser/browser-dummy-client.cpp
Normal file
@ -0,0 +1,87 @@
|
||||
/******************************************************************************
|
||||
Copyright (C) 2026 by Matt Gajownik <matt@volunteer.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-dummy-client.hpp"
|
||||
|
||||
CefRefPtr<CefCommandHandler> BrowserDummyClient::GetCommandHandler()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
CefRefPtr<CefRequestHandler> BrowserDummyClient::GetRequestHandler()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
CefRefPtr<CefLifeSpanHandler> BrowserDummyClient::GetLifeSpanHandler()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
bool BrowserDummyClient::OnChromeCommand(CefRefPtr<CefBrowser>, int, cef_window_open_disposition_t)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BrowserDummyClient::IsChromeAppMenuItemVisible(CefRefPtr<CefBrowser>, int)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BrowserDummyClient::IsChromeToolbarButtonVisible(cef_chrome_toolbar_button_type_t)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BrowserDummyClient::IsChromePageActionIconVisible(cef_chrome_page_action_icon_type_t)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BrowserDummyClient::IsChromeAppMenuItemEnabled(CefRefPtr<CefBrowser>, int)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BrowserDummyClient::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 *)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void BrowserDummyClient::OnAfterCreated(CefRefPtr<CefBrowser> browser)
|
||||
{
|
||||
if (browser && browser->GetHost()) {
|
||||
browser->GetHost()->CloseBrowser(false);
|
||||
}
|
||||
}
|
||||
|
||||
bool BrowserDummyClient::OnOpenURLFromTab(CefRefPtr<CefBrowser>, CefRefPtr<CefFrame>, const CefString &,
|
||||
CefRequestHandler::WindowOpenDisposition, bool)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BrowserDummyClient::OnBeforeBrowse(CefRefPtr<CefBrowser>, CefRefPtr<CefFrame>, CefRefPtr<CefRequest>, bool, bool)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
64
obs-studio/plugins/obs-browser/browser-dummy-client.hpp
Normal file
64
obs-studio/plugins/obs-browser/browser-dummy-client.hpp
Normal file
@ -0,0 +1,64 @@
|
||||
/******************************************************************************
|
||||
Copyright (C) 2026 by Matt Gajownik <matt@volunteer.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"
|
||||
|
||||
class BrowserDummyClient : public CefClient,
|
||||
public CefCommandHandler,
|
||||
public CefRequestHandler,
|
||||
public CefLifeSpanHandler {
|
||||
public:
|
||||
virtual CefRefPtr<CefCommandHandler> GetCommandHandler() override;
|
||||
virtual CefRefPtr<CefRequestHandler> GetRequestHandler() override;
|
||||
virtual CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() override;
|
||||
|
||||
/* CefCommandHandler */
|
||||
virtual bool OnChromeCommand(CefRefPtr<CefBrowser> browser, int command_id,
|
||||
cef_window_open_disposition_t disposition) override;
|
||||
virtual bool IsChromeAppMenuItemVisible(CefRefPtr<CefBrowser> browser, int command_id) override;
|
||||
virtual bool IsChromeToolbarButtonVisible(cef_chrome_toolbar_button_type_t button_type) override;
|
||||
|
||||
virtual bool IsChromePageActionIconVisible(cef_chrome_page_action_icon_type_t icon_type) override;
|
||||
|
||||
virtual bool IsChromeAppMenuItemEnabled(CefRefPtr<CefBrowser> browser, int command_id) override;
|
||||
|
||||
/* CefLifeSpanHandler */
|
||||
virtual bool OnBeforePopup(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame,
|
||||
#if CHROME_VERSION_BUILD >= 6834
|
||||
int,
|
||||
#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;
|
||||
|
||||
virtual void OnAfterCreated(CefRefPtr<CefBrowser> browser) override;
|
||||
|
||||
/* CefRequestHandler */
|
||||
virtual bool OnOpenURLFromTab(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame,
|
||||
const CefString &target_url,
|
||||
CefRequestHandler::WindowOpenDisposition target_disposition,
|
||||
bool user_gesture) override;
|
||||
|
||||
virtual bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame,
|
||||
CefRefPtr<CefRequest> request, bool user_gesture, bool is_redirect) override;
|
||||
|
||||
IMPLEMENT_REFCOUNTING(BrowserDummyClient);
|
||||
};
|
||||
71
obs-studio/plugins/obs-browser/browser-scheme.cpp
Normal file
71
obs-studio/plugins/obs-browser/browser-scheme.cpp
Normal file
@ -0,0 +1,71 @@
|
||||
/******************************************************************************
|
||||
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 "ip-string.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";
|
||||
|
||||
std::string filePath = path.substr(1);
|
||||
|
||||
std::string checkString = path.substr(path.find_first_not_of("/"));
|
||||
checkString = checkString.substr(0, checkString.find_first_of("/"));
|
||||
|
||||
// An IP address should never be a valid path for CreateForFile normally, but in some cases an OS
|
||||
// can resolve one as such. As an extra safeguard, we prevent any IP addresses in the path.
|
||||
if (checkForIpv4String(checkString)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (checkForIpv6String(checkString)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CefRefPtr<CefStreamReader> stream = CefStreamReader::CreateForFile(filePath);
|
||||
if (!stream) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CefString mimeType = CefGetMimeType(fileExtension);
|
||||
if (mimeType.empty()) {
|
||||
mimeType = "application/octet-stream";
|
||||
}
|
||||
|
||||
return new CefStreamResourceHandler(mimeType, stream);
|
||||
}
|
||||
31
obs-studio/plugins/obs-browser/browser-scheme.hpp
Normal file
31
obs-studio/plugins/obs-browser/browser-scheme.hpp
Normal file
@ -0,0 +1,31 @@
|
||||
/******************************************************************************
|
||||
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);
|
||||
};
|
||||
19
obs-studio/plugins/obs-browser/browser-version.h
Normal file
19
obs-studio/plugins/obs-browser/browser-version.h
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#define OBS_BROWSER_VERSION_MAJOR 2
|
||||
#define OBS_BROWSER_VERSION_MINOR 27
|
||||
#define OBS_BROWSER_VERSION_PATCH 0
|
||||
|
||||
#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)
|
||||
@ -0,0 +1,3 @@
|
||||
if (( ! ${+_loglevel} )) typeset -g _loglevel=1
|
||||
|
||||
if (( _loglevel > 2 )) print -PR -e -- "${CI:+::debug::}%F{220}DEBUG: ${@}%f"
|
||||
@ -0,0 +1,3 @@
|
||||
local icon=' ✖︎ '
|
||||
|
||||
print -u2 -PR "${CI:+::error::}%F{1} ${icon} %f ${@}"
|
||||
@ -0,0 +1,16 @@
|
||||
autoload -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 ${@}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
if (( ! ${+_loglevel} )) typeset -g _loglevel=1
|
||||
|
||||
if (( _loglevel > 0 )) {
|
||||
local icon=' =>'
|
||||
|
||||
print -PR "%F{4} ${(r:5:)icon}%f %B${@}%b"
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
if (( ! ${+_loglevel} )) typeset -g _loglevel=1
|
||||
|
||||
if (( _loglevel > 0 )) {
|
||||
local icon=''
|
||||
|
||||
print -PR " ${(r:5:)icon} ${@}"
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
if (( ! ${+_loglevel} )) typeset -g _loglevel=1
|
||||
|
||||
if (( _loglevel > 0 )) {
|
||||
local icon=' >'
|
||||
|
||||
print -PR "%F{2} ${(r:5:)icon}%f ${@}"
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user