For some unknown reason, GetWindow will not traverse the entire window
tree. It could be due to Microsoft purposely hiding certain UWP
windows, though the reason is unknown. For some equally unknown reason
FindWindowEx does work in its place.
This fixes the issue of not being able to find/capture certain windows,
such as halo 5: forge.
D3D12 capture does not take in to account when multiple backbuffers are
in used. With previous versions of Direct3D there was no need to do
this, but with D3D12 you must explicitly capture each specific
backbuffer currently in use.
If capturing a UWP window, do not fall back to matching windows with the
same window class if the exact window is not found, as this will get any
other UWP window on the system (due to the fact that they all have the
same window class name).
Because the hook cannot get the window handle of UWP windows, fall back
to using the window handle stored in the game capture source itself if
it's unavailable from the hook.
Under certain circumstances, the program may not be able to acquire the
window thread ID for a UWP process, but will be able to acquire the
process ID. In this case, it should soft fail and retry, rather than
assume it's unacquirable and stop trying to reacquire.
The "attempting to hook [executable]" message would not display the
correct executable if it's fallen back to a different window with the
same window class.
Now that the game capture hook creates and controls all the
synchronization objects, it's no longer possible to expect that the hook
is fully loaded by the time game capture tries to initialize it. In
that case, allow game capture to retry the hook for a few frames before
assuming something failed.
This detects whether the target process is a UWP process, and then uses
the open_app_* functions for mutexes/events/mapping. Also slightly
refactors named object open functions.
The only way to open named kernel objects within a UWP "app" is to get
the AppContainer SID, and then open the objects with their full
system namespace names via undocumented NT functions.
All named objects (including file mapped shared memory) need to be
created within the hook itself due to the fact that UWP programs cannot
access named objects outside of the UWP process.
Because shared memory needs to be created within the hook, the capture
loop cannot start until the shared memory has been filled with valid
data. Creating an additional "initialize" event fixes this issue.
Additionally, changed the way that named kernel objects are
opened/created. Before, there were functions that would first try to
open named objects and then implicitly create them if opening failed
(assuming that if the hook didn't create it first, game capture would),
now it's been changed so that you can only either explicitly open or
create.
To check to make sure game capture is still active in the capture
program, it currently uses a named event, and then it checks to see if
that named event exists. However with UWP programs, you can't open a
named event outside of the UWP process. FindWindow on the other hand
does work, so instead of checking to see if a named kernel object
exists, create a window and check to see if that window exists.
The "main" windows detected for UWP programs are basically to help
sandbox the programs -- they run in the ApplicationFrameHost process and
help reduce the possibility of other programs trying to access the
actual process window, which is a child window.
To bypass this, go through the list of child windows for the
ApplicationFrameHost window, and then find the one that's attached to
a different process; that different process will always be the target,
and will allows us to open the actual process of the UWP program.
Instead of calling OpenThread, use DuplicateHandle with the minimal
access rights (SYNCHRONIZE) to be able to get the current thread handle
within a UWP program.
This prevents issues with using standard C functions, where microsoft
would otherwise spit out pointless warnings to encourage using
microsoft-specific functions instead.
(Note: This commit also modifies the ipc-util/seg-service modules)
When compiling the final project, always compile
ipc-util/get-graphics-offsets/graphics-hook/inject-helper/seg-service
with static MSVC runtimes to prevent the need of requiring the MSVC
runtimes for both architectures.
Whenever a cursor is captured and the cursor icon changes, it creates a
new texture. This isn't particularly optimal, so instead just store a
cache of cursor textures (based on size), and make the textures dynamic.
Doing this will prevent unnecessary texture reallocation.
Even if the hotkey is not enabled, always allow configuration of the
hotkey. Fixes a bug where the hotkey configuration settings would not
save if the settings were changed.
Annoyingly this means that the hotkey will still be shown to the user,
possibly confusing the user as to whether they can use it, but for the
time being it's better than having their hotkey configuration removed
each time they change the mode.
It's supposed to look for patch segments in ascending order, from the
smallest offset to the largest offset. Patch type/comparison is
identical to the one it's being swapped with, so only the offsets need
to be swapped.
Prevents the common problem of injecting in to certain processes and
getting the hook DLL "stuck":
- windows explorer
- steam
- battle.net
- gog galaxy
- skype
- uplay
- origin
- microsoft visual studio
- task manager
- league of legends lobby window
- windows 10 system settings window
Changed the first property of game capture to be a "mode" list (with
"any fullscreen window", "specific window", and "hotkey").
When hotkey mode is set, it'll add a hotkey pair to hotkey settings to
activate/deactivate game capture. When the hotkey to activate is
pressed, it'll treat the current foreground window as the target window
similar to "selected window" mode; it'll keep trying to capture the same
window even if the window or its application closes/reopens, and will
continue to do so until deactivated via the deactivate hotkey, or until
a new window is set via the activate hotkey.
This reverts commit 4c505e7030.
Reverting this for the time being due to issues with quakelive. This
will be more thoroughly tested and hopefully added again.
Tested using FTL (steam): SwapBuffers ultimately calls wgl_swap_buffers
causing an additional copy which just isn't necessary
This also causes game capture to sometimes capture overlays even when
not intended
Removing this union fixes the internal compile error that would occur on
visual studio 2015 update 2 and above when these variables were all in a
union.
Fixes a bug where if a D3D9 program recreates its device the capture
would become invalid. Certain games (especially blizzard games) will
completely recreate their Direct3D device if a critical D3D9 error
occurs.
This reverts commit 8d520b970d.
This can actually cause a hard lock due to the windows API when
destroying window capture. When the graphics thread locks the source
list for doing tick or render, and then the UI thread tries to destroy a
source, the UI thread will wait for the graphics thread to complete
rendering/ticking of sources. The video_tick of window capture would
then check windows in the same process and try to query the window's
name via GetWindowText. However, GetWindowText is synchronous, and will
not return until the window event has been processed by the UI thread,
so it will perpetually lock because the two threads are waiting for each
other to finish.
Prevents game capture from acting as a global source. This fixes an
issue where a game capture in another scene could capture a window and
prevent a separate game capture in the current scene from being able to
capture that same window.
Completely shut down monitor capture when it's not being shown in the
program (for example in a different scene). This fixes an issue where
it would cause lag when a game enters fullscreen mode.
When using a chain hook method (forward or reverse), it was unwisely
assumed that the previous hook in the chain would not overwrite new
hooks when it's called. When the game capture hook calls the previous
hook in the chain, certain other programs that hook (in this case,
rivatuner on-screen display) would overwrite the hook with older data
and erase the game capture hook, causing it to only capture the first
frame and then never capture again.
This patch ensures that the hook is always saved before calling the next
hook in the chain and then restored after the call returns. It also
preserves any new hooks that may be added on top of it at any point.
Originally this on by default, but then was changed to being off by
default because it was thought that there were permission issues, but it
turned out that the permission issues were a separate bug, so it's safe
to have this be default to on again.
This is a fast/immediate solution to a possible bug with caching the DLL
versions for game capture hook addresses - may as well just reload game
capture hook addresses each time the program is run for the time being
just to be safe. Load time will increase a little for the time being
but it's worth it to prevent any issues with game capture.
Certain types of sources (display captures, game captures, audio
device captures, video device captures) should not be duplicated. This
capability flag hints that the source prefers references over full
duplication.
Darkest dungeon uses an unusual technique for drawing its frames: a
fixed 1920x1080 frame buffer used in place of the backbuffer, which is
then stretched to fit the size of the screen (whether the screen is
bigger or smaller than the actual texture).
The custom frame would cause glReadBuffer to initially fail with an
error. When this happens, their custom frame buffer is in use, so all
that needs to be done is simply reset the capture and force the current
output size to 1920x1080 while that custom frame is in use.
They presumably did this in order to ensure the game looks the same at
any resolution. Instead of having to use power-of-two sprites and
mipmaps for every single game sprite and stretch/skew each of them
(which would risk the final output "not looking quite right" at
different resolutions), they simply use non-pow-2 sprites with no
mipmaps and render them all on to one texture of a fixed size and then
stretch that final output texture. That ensures that the actual
composite of the game still looks the same at any resolution, while
reducing texture memory by not requiring each sprite to use a
power-of-two texture and mipmaps.
Some games don't catch GL errors via glGetError, so there's a
possibility that an error will pass through to the capture calls,
causing a false failure.
The most simple solution is to just clear the error flag on each capture
call.
The virtual address table values for Reset/ResetEx can sometimes point
to functions that are in libraries outside of D3D8.dll and D3D9.dll, and
will cause a crash if used. Instead, just hook Reset/ResetEx when one
of the Present* functions are called.
API removed:
--------------------
gs_effect_t *obs_get_default_effect(void);
gs_effect_t *obs_get_default_rect_effect(void);
gs_effect_t *obs_get_opaque_effect(void);
gs_effect_t *obs_get_solid_effect(void);
gs_effect_t *obs_get_bicubic_effect(void);
gs_effect_t *obs_get_lanczos_effect(void);
gs_effect_t *obs_get_bilinear_lowres_effect(void);
API added:
--------------------
gs_effect_t *obs_get_base_effect(enum obs_base_effect effect);
Summary:
--------------------
Combines multiple near-identical functions into a single function with
an enum parameter.
Instead of using shell functions to get the windows system directory,
use the kernel32 functions (GetSystemDirectory and
GetSystemWow64Directory). Reduces a bit of unnecessary overhead.
The new 'offset' value was not being passed back to the caller, which
caused the caller to continue to use the old value and thus would cause
an invalid hook and crash.
If the GL capture part of the game capture hook fails to initialized for
whatever reason, it will go in to an infinite reacquire loop. If it
fails to initialize shared texture capture, try shared memory capture
instead.
For game capture, if a game is running at for example 800 FPS and limit
capture framerate is off, it would try to capture all 800 of those
frames, dramatically reducing performance more than what would ever be
necessary.
When limit capture framerate is off, instead of capturing all frames,
capture frames at an interval of twice the OBS FPS, identical to how
OBS1 works by default. This should greatly increase performance under
that circumstance.
API changed from:
obs_source_info::get_name(void)
obs_output_info::get_name(void)
obs_encoder_info::get_name(void)
obs_service_info::get_name(void)
API changed to:
obs_source_info::get_name(void *type_data)
obs_output_info::get_name(void *type_data)
obs_encoder_info::get_name(void *type_data)
obs_service_info::get_name(void *type_data)
This allows the type data to be used when getting the name of the
object (useful for plugin wrappers primarily).
NOTE: Though a parameter was added, this is backward-compatible with
older plugins due to calling convention. The new parameter will simply
be ignored by older plugins, and the stack (if used) will be cleaned up
by the caller.
..This is rather embarrassing. I used the parameter variable and the
actual variable that I wanted to used went completely unused. Would
static analysis catch something like this, I wonder? Would probably
have to be really good static analysis.
This reverts commit 74354dc4cf. I really
shouldn't have modified this, especially not in this way. Was the wrong
approach. The thing I was trying to fix was very rare as well.
I've come to realize that it's probably not wise to deviate from the
original version's functionality due to the fact that the original
version works without issues. I'm wondering if some of the capture
problems have been due to the fact that the direct hook method (via
CreateRemoteThread) was removed, so I put it back in, made it default,
and added an option to use anti-cheat compatibility just like in the
original version.
When hooking 64bit functions, sometimes the offset between the function
being hooked and the hook itself can be large enough to where it
requires a 64bit offset to be used. However, because a 64bit jump
requires overwriting so many code instructions in the function, it can
sometimes overwrite code in to an adjacent function, thereby causing a
crash.
The 64bit hook bounce (created by R1CH) is designed to prevent using
very long jumps in the target by creating executable memory within a
32bit offset of that target, and then writing it with the 64bit long
jump instruction instead. Then in the target function, it will jump to
that memory instead, thus forcing the actual hooked function to use a
32bit hook instead of a 64bit hook, and using at most 5 bytes for the
actual hook, preventing any likelihood of it overwriting an adjacent
function.
If capture starts too quickly, the file mapping will return 2, which
means file not found, and it would then reset the capture and try again.
Sometimes this would result in long intervals where it wouldn't capture.
This fixes the issue by simply making game capture retry if file mapping
returns error number 2.
This fixes an issue primarily with filter rendering: when capturing
windows and displays, their alpha channel is almost always 0, causing
the image to be completely invisible unintentionally. The original fix
for this for many sources was just to turn off the blending, which would
be fine if you're not rendering any filters, but filters will render to
render targets first, and that lack of alpha will end up carrying over
in to the final image.
This doesn't apply to any mac captures because mac actually seems to set
the alpha channel to 1.
I had this issue where IDXGISwapChain::ResizeBuffers would fail in the
hooks, causing games to crash when they resized their backbuffers
because ResizeBuffers would return an 'invalid call' HRESULT value. In
the ResizeBuffers documentation it says that it will only happen if a
backbuffer currently has any outstanding references, but there's no way
this would happen unless ResizeBuffers internally calls Present or vise
versa.
After ResizeBuffers has been called, the very first call to Present will
somehow seemingly invalidate and/or destroy the current backbuffer.
It's very strange, but that seems to be what's going on, at least for
the game I was testing. So if you are performing a post-overlay
capture, then you must ignore the capture on the very first call to
Present.
It's Microsoft's code so you can't really know what's going on, you just
have to work around these strange issues seemingly in the dark.
Apparently someone dumb (aka me) neglected to properly handle the inline
graphics hook API functions. You're not supposed to 'extern' inline
functions, they need to be defined for each file when ever they're used.
Apparently neglected to use the reference operator. I think this may
partially be one of the reasons why many developers still choose to use
pointers instead of references, but fortunately an actual GOOD compiler
warns about this (aka anything but vc)
Clears up a warning (to prevent && and || confusion), and clarifies what
specifically the if statement is trying to accomplish (check to see if
the capture is valid)
The HWND type is a void pointer, but HWND values are global and always
32bit despite, so casting to 32bit can cause cast warnings on actual
good compilers like gcc via mingw. This change correctly handles the
casting to 32bits without producing unwanted warnings or errors on
mingw.
win-capture should not postfix .lib to psapi.
The graphics hook also requires psapi when linking.
Also change some link libs as mingw-w64 libraries are not postfixed
.lib.
If the PSAPI_VERSION macro is not set to 1 when using
GetProcessImageFileName, it will attempt to import it as
K32GetProcessImageFileName from kernel32.dll instead of psapi.dll, which
breaks compatibility with vista and xp.
Having macros that state what these numbers mean is much more ideal than
just having a random number thrown in there, wondering why it was used
and what its purpose is (magic numbers).
The activate button is just silly for configuration in retrospect. It's
confusing to users, and was even confusing to some other developers.
Instead of using an 'Activate' button for game capture every time you
want to capture a window, just make the 'window' list have a default 'no
window' value (empty), and then have it always active when an actual
window is selected. The way syphon handles this on mac is actually
where I took the idea from (as suggested by Palana).
With the new code that checks to see if the source is visible, I didn't
realize that I actually didn't set the source variable, so it would end
up never actually drawing.
If shared memory file mapping fails, I've found that it's somewhat
normal due to something in windows -- usually the capture will always
eventually start up after a few tries. Only seems to apply to some
games though, for example seems to happen with counterstrike a lot for
some strange reason. Capture always eventually starts back up though.
I remember seeing this with OBS1 as well in many cases but always
thought it was some sort of fluke
If using the auto-fullscreen feature to hook in to a fullscreen, I found
that if you don't wait a few seconds before initializing the hook that
you can catch the process when it's just starting up and loading
important libraries (especially things such as steam/uplay/etc), which
can cause a little bit of interference with the process and on rare
occasions cause it to crash.
To help prevent the likelihood of that happening, this just makes it so
that the hook waits at least 3 seconds before even attempting to inject
the hook when using auto-fullscreen mode. After some extensive testing
I haven't had any issues since.
The design to not retry the hooks on most general error is just bad.
There are plenty of legitimate cases where it should retry the hook.
This changes it so that if a general failure occurs or if it isn't
capturing when the inject helper exits, it retries and increases the
length of time between retries.
Variables that track time should not have the name 'interval', they
should have the name 'time' instead so it's crystal clear that the
variable is tracking time.
Adds a variable 'retry_interval' to game capture that allows the
interval at which game capture checks to update to longer intervals if
the hook initialization has some sort of failure.
The reason why I want to do this is because I don't really like it when
the hook updates too often in failure, it just leads to log file spam
that I feel can be reduced, and it frequent updates feel a bit invasive.
I just generally feel more comfortable reducing the interval at which
the hook retries after failure.
This makes a minor adjustment to the interval at which the inject helper
tries to post the inject message to the target process. Only 2 seconds
before, now up to 4 seconds, with the PostThreadMessage called every
half second for the duration.
The reason I did this is because I noticed that on rare occasions that
it wouldn't hook due to the low interval; usually just because the
target process is busy and isn't able to process its message queue, and
therefor the hook wouldn't go through due to the fact that
SetWindowsHookEx won't inject until the set event has occurred. The
inject helper program would just close before the thread message had
finally been processed, which would cancel the SetWindowsHookEx hooking.
The code neglected to take in to account that start_capture can also be
called when the texture updates its size/format in the hook and 'ready'
is signaled again, so it's possible that existing variables in the game
capture structure could be overwritten with new ones unintentionally.
The game capture 'Activate' button is likely to fool users in to
thinking it's not actually active if the game capture displays black, so
if it's active, rename the button to 'Reactivate' in order to sort of
hint at the user that it's actually active.
This is a bit of an optimization to reduce load a little bit if any of
the video capture sources are not currently being displayed on the
screen. They will simply not capture or update their texture data if
they are not currently being shown anywhere.
The mac and window game capture sources don't really apply due to the
fact that their textures aren't updated on the source's end (they update
inside of the hooks).
Uses the output duplicator API in order to get a high performance
monitor capture on windows 8+. This is actually designed to be
interchangeable with regular GDI-based monitor capture (uses the same
source id).
This adds the windows version of game capture.
New features:
- An option to hook any fullscreen application automatically (that
doesn't have borders) so that no specific window configuration is
required. Definitely a sorely needed feature
- An option to force memory capture for the sake of compatibility with
things such as SLI, multi-adapter setups (usually laptops), as well as
the ability to be used with the OpenGL renderer
- An optimization option to force scaling on the GPU before texture
transfer, reducing the transfer bandwidth (which is especially
important for compatibility capture)
- An optimization option to limit framerate to the current OBS framerate
to improve capture performance (mostly useful for compatibility
capture)
- An option to capture third-party overlays (such as steam)
- Logging improvements, game capture log will now be sent via pipe
instead of written to a separate file, making diagnosing problems a
little bit easier
This library is a completely refactored and rewritten version of the
original graphics hook. The code is more clean, readable, and has a
variety of new features, such as scaling and forcing memory capture.
Currently, only D3D9, 10, and 11 are implemented. (This commit may be
updated on this branch)
Before, game capture would find addresses to important graphics
functions by creating a graphics context for the desired API inside of
the hook, and then find the function addresses that way.
The big problem with that is that the context could often cause the
hooked application to crash, especially if another hook was active.
This bypasses that entire need by a simple console application that
creates the contexts, finds the hook address offsets and then returns
them via console output.
This header contains global defines, structures, and helper inline
functions for the graphics hook that will be shared between game
capture, the hook, and the get-graphics-addrs helper application.
These functions allow the safe hooking of windows functions,
specifically windows API functions that may or may not have built-in
machine code to help aid in reverse chain hooks.
If a new hook is applied to an existing forward hook, that hook will be
preserved to prevent that new hook's data from being removed
unintentionally.
Hopefully with all these precautions this will reduce the likelihood of
crashes and abnormal hook behavior, while allowing existing hooks to be
preserved, and allowing new hooks to be applied.
This fixes a bug where if INCLUDE_MINIMIZED was set and the window size
was (0, 0), the window would still be excluded from the resulting list
that was created.
This adds obfuscation functions primarily for use with GetProcAddress.
This takes an obfuscated string and uses a simple integer key to
de-obfuscate it to the intended function name string, which is then
loaded dynamically using GetProcAddress.
This is typically only used with functions such as OpenProcess,
SetWindowsHookEx, and the like, which can often be misinterpreted the
wrong way by security programs if those strings are found within the
strings segment of a scanned executable.
When getting the class/title/exe of a particular window handle in the
build_window_strings function, always set the class/title/exe pointers
to null to prevent any potential references to invalid values if any of
them do not happen to be set for whatever reason.
Because other capture methods may end up needing to share this code,
separate the window finding source code to window-helpers.c and
window-helpers.h.
This include a function to fill out a property list with windows, a
function to find a window based upon priority/title/class/exe, and a
function to decode the window title/class/exe strings from a window
setting string.
Typedef pointers are unsafe. If you do:
typedef struct bla *bla_t;
then you cannot use it as a constant, such as: const bla_t, because
that constant will be to the pointer itself rather than to the
underlying data. I admit this was a fundamental mistake that must
be corrected.
All typedefs that were pointer types will now have their pointers
removed from the type itself, and the pointers will be used when they
are actually used as variables/parameters/returns instead.
This does not break ABI though, which is pretty nice.
API Removed:
- graphics_t obs_graphics();
Replaced With:
- void obs_enter_graphics();
- void obs_leave_graphics();
Description:
obs_graphics() was somewhat of a pointless function. The only time
that it was ever necessary was to pass it as a parameter to
gs_entercontext() followed by a subsequent gs_leavecontext() call after
that. So, I felt that it made a bit more sense just to implement
obs_enter_graphics() and obs_leave_graphics() functions to do the exact
same thing without having to repeat that code. There's really no need
to ever "hold" the graphics pointer, though I suppose that could change
in the future so having a similar function come back isn't out of the
question.
Still, this at least reduces the amount of unnecessary repeated code for
the time being.
This functionality can now be handled automatically because locale can
now be freed seaparately from obs_module_unload with
obs_module_free_locale, which is called automatically when the module is
being freed.
Changed API:
- char *obs_find_plugin_file(const char *sub_path);
Changed to: char *obs_module_file(const char *file);
Cahnge it so you no longer need to specify a sub-path such as:
obs_find_plugin_file("module_name/file.ext")
Instead, now automatically handle the module data path so all you need
to do is:
obs_module_file("file.ext")
- int obs_load_module(const char *name);
Changed to: int obs_open_module(obs_module_t *module,
const char *path,
const char *data_path);
bool obs_init_module(obs_module_t module);
Change the module loading API so that if the front-end chooses, it can
load modules directly from a specified path, and associate a data
directory with it on the spot.
The module will not be initialized immediately; obs_init_module must
be called on the module pointer in order to fully initialize the
module. This is done so a module can be disabled by the front-end if
the it so chooses.
New API:
- void obs_add_module_path(const char *bin, const char *data);
These functions allow you to specify new module search paths to add,
and allow you to search through them, or optionally just load all
modules from them. If the string %module% is included, it will
replace it with the module's name when that string is used as a
lookup. Data paths are now directly added to the module's internal
storage structure, and when obs_find_module_file is used, it will look
up the pointer to the obs_module structure and get its data directory
that way.
Example:
obs_add_module_path("/opt/obs/my-modules/%module%/bin",
"/opt/obs/my-modules/%module%/data");
This would cause it to additionally look for the binary of a
hypthetical module named "foo" at /opt/obs/my-modules/foo/bin/foo.so
(or libfoo.so), and then look for the data in
/opt/obs/my-modules/foo/data.
This gives the front-end more flexibility for handling third-party
plugin modules, or handling all plugin modules in a custom way.
- void obs_find_modules(obs_find_module_callback_t callback, void
*param);
This searches the existing paths for modules and calls the callback
function when any are found. Useful for plugin management and custom
handling of the paths by the front-end if desired.
- void obs_load_all_modules(void);
Search through the paths and both loads and initializes all modules
automatically without custom handling.
- void obs_enum_modules(obs_enum_module_callback_t callback,
void *param);
Enumerates currently opened modules.