67#define MOD_NAME "[screen win] "
68#define FILTER_UPSTREAM_URL "https://github.com/rdp/screen-capture-recorder-to-video-windows-free/releases"
87 "stretch_mode_high_quality_if_1",
89 "disable_aero_for_vista_plus_if_1",
90 "track_new_x_y_coords_each_frame_if_1",
91 "capture_mouse_default_1",
92 "capture_foreground_window_if_1",
94 "millis_to_sleep_between_poll_for_dedupe_changes",
95 "capture_transparent_windows_including_mouse_in_non_aero_if_1_causes_annoying_mouse_flicker",
96 "hwnd_to_track_with_window_decoration",
97 "capture_particular_display_number_starting_at_zero"
103 char desc[] =
"Windows " TBOLD(
"screen capture")
" can be used to capture the whole desktop in Windows.\n\n"
104 "It uses dynamically loaded DShow filter to get the screen data. Depending on the system configuration, "
105 "it may be required to confirm the filter registration with elevated permissions. In that case the plugin "
106 "remains registered permanently (you can unregister it with \"" TBOLD(
":unregister")
"\" (otherwise it is "
107 "unloaded in the end. You can also download and install it from " FILTER_UPSTREAM_URL " to get latest "
112 TBOLD(
TRED(
"\t-t screen")
"[:clear_prefs][:size=<w>x<h>][:fps=<f>][other-opts]")
"\n");
114 "-t screen:unregister")
"\n");
117 color_printf(
TBOLD(
"\tclear_prefs")
" - clear screen capture preferences from registry (must be first opt)\n");
118 color_printf(
TBOLD(
"\tother-opts")
" - one of screen-capture-recorder opts (with value):");
128 "default/registry values\n",
uv_argv[0]);
130 "prefs and capture with default values\n",
uv_argv[0]);
132 " - capture VGA-sized video and dropping previous "
135 " - capture using saved values with x/y offset 200\n\n",
149 snprintf(cards[0].
name,
sizeof cards[0].
name,
"Screen capture");
150 *available_cards = cards;
153#define SCREEN_CAP_REG_TREE "Software\\screen-capture-recorder"
154#define REG_FRIENDLY_NAME "\\HKEY_CURRENT_USER\\" SCREEN_CAP_REG_TREE
160 LONG regOpenResult = RegOpenKeyEx(
162 if (regOpenResult != ERROR_SUCCESS) {
167 MSG(INFO,
"Using following configuration from Windows Registry:\n");
170 DWORD bufferSize = MAX_PATH;
171 TCHAR valueName[MAX_PATH];
173 DWORD valueSize = MAX_PATH;
175 while (RegEnumValue(hKey, index, valueName, &bufferSize, NULL,
176 &valueType, data, &valueSize) == ERROR_SUCCESS) {
177 if (valueType == REG_DWORD) {
178 MSG(INFO,
"Option: %s (REG_DWORD), Value: %lu\n",
179 valueName, *(DWORD *) data);
180 }
else if (valueType == REG_SZ) {
181 MSG(INFO,
"Option: %s (REG_SZ), Value: %s\n", valueName,
184 MSG(INFO,
"Option: %s (unknown type), Data Size: %lu\n",
185 valueName, valueSize);
189 bufferSize = MAX_PATH;
190 valueSize = MAX_PATH;
201 if (err == ERROR_SUCCESS) {
207 get_win32_error(err));
217 "Use `-t screen:clear_prefs` to erase.\n");
222 NULL, REG_OPTION_NON_VOLATILE,
223 KEY_ALL_ACCESS, NULL, &hKey, NULL);
224 if (res != ERROR_SUCCESS) {
227 KEY_ALL_ACCESS, &hKey);
228 if (res != ERROR_SUCCESS) {
232 DWORD val_dword =
val;
233 return RegSetValueExA(hKey,
key, 0L, REG_DWORD, (
BYTE *) &val_dword,
sizeof val_dword);
240 long val = strtol(val_c, &endptr, 0);
241 if (*endptr !=
'\0') {
246 if (res == ERROR_SUCCESS) {
249 MSG(ERROR,
"Cannot set %s=%ld: %s\n",
key,
val, get_win32_error(res));
256 if (strchr(
fmt,
'=') == NULL) {
257 MSG(ERROR,
"No value for key %s\n",
fmt);
261 *val_c = strchr(
fmt,
'=') + 1;
262 *strchr(
fmt,
'=') =
'\0';
270 MSG(ERROR,
"Unknown parameter: %s\n",
fmt);
276 if (!
fmt ||
fmt[0] ==
'\0') {
279 char *fmt_c = strdup(
fmt);
280 assert(fmt_c != NULL);
286 while ((tok = strtok_r(tmp,
":", &save_ptr)) != NULL) {
290 if (
IS_KEY_PREFIX(tok,
"size") && strchr(tok,
'x') != NULL) {
291 char *width = strchr(tok,
'=') + 1;
292 char *height = strchr(tok,
'x') + 1;
293 *strchr(tok,
'x') =
'\0';
300 }
else if (strstr(tok,
"width=") != NULL) {
301 MSG(WARNING,
"Deprecated width=, use size=\n");
302 key =
"capture_width";
303 val_c = tok + strlen(
"width=");
304 }
else if (strstr(tok,
"height=") != NULL) {
305 MSG(WARNING,
"Deprecated height=, use size=\n");
306 key =
"capture_height";
307 val_c = tok + strlen(
"height=");
308 }
else if (strstr(tok,
"fps=") != NULL) {
309 key =
"default_max_fps";
310 val_c = tok + strlen(
"fps=");
325typedef HRESULT __stdcall (*
func)();
327#define CHECK_NOT_NULL(cmd, err_action) do { if ((cmd) == NULL) { log_msg(LOG_LEVEL_ERROR, MOD_NAME "%s\n", #cmd); err_action; } } while(0)
343 void (*
deleter)(
void *) = NULL;
349 for (
int i = 0; i <
count; ++i) {
350 if (strcmp(cards[i].
name,
"screen-capture-recorder") == 0) {
361 HMODULE screen_cap_lib = LoadLibraryA(
"screen-capture-recorder-x64.dll");
363 DWORD err_code = GetLastError();
364 MSG(ERROR,
"Failed to load screen capture dll: %ld - %s\n",
365 err_code, get_win32_error(err_code));
369 func register_filter;
370 CHECK_NOT_NULL(register_filter = (
func)(
void *) GetProcAddress(screen_cap_lib,
"DllRegisterServer"), FreeLibrary(screen_cap_lib);
return -1);
371 HRESULT res = register_filter();
372 FreeLibrary(screen_cap_lib);
373 if (SUCCEEDED(res)) {
376 FreeLibrary(screen_cap_lib);
378 if (res == E_ACCESSDENIED) {
381 HINSTANCE ret = ShellExecute( NULL,
383 uv_argv[0],
" -t screen:register_elevated",
387 if ((INT_PTR) ret > 32) {
405 HMODULE screen_cap_lib = NULL;
407 LoadLibraryA(
"screen-capture-recorder-x64.dll"),
411 screen_cap_lib,
"DllUnregisterServer"),
412 FreeLibrary(screen_cap_lib);
415 if (SUCCEEDED(res)) {
416 MSG(NOTICE,
"Filter unregistered successfully\n");
419 MSG(ERROR,
"Cannot unregister filter, ret = %ld (%s)\n",
420 res, hresult_to_str(res));
440 for (
int i = 0; i <
uv_argc; ++i) {
444 snprintf(tmp,
sizeof tmp,
"\"screen:child:%s\" ",
uv_argv[i] + 6);
446 snprintf(tmp,
sizeof tmp,
"\"screen:child\" ");
448 snprintf(tmp,
sizeof tmp,
"\"%s\" ",
uv_argv[i]);
450 strncat(cmd, tmp,
sizeof cmd - strlen(cmd) - 1);
452 if (strlen(cmd) ==
sizeof cmd - 1) {
455 STARTUPINFO si = { 0 };
456 PROCESS_INFORMATION pi = { 0 };
458 const bool ret = CreateProcess(NULL, cmd, NULL, NULL,
FALSE, 0, NULL,
461 WaitForSingleObject(pi.hProcess, INFINITE);
470 if (strcmp(cfg,
"help") == 0 || strcmp(cfg,
"fullhelp") == 0) {
474 if (strcmp(cfg,
"register_elevated") == 0) {
479 if (strcmp(cfg,
"unregister") == 0) {
480 HINSTANCE inst = ShellExecute(NULL,
"runas",
uv_argv[0],
481 " -t screen:unregister_elevated",
484 INT_PTR ret = (INT_PTR) inst;
488 MSG(ERROR,
"Elevated subshell exec failed, res = %lld (%s)\n",
489 ret, get_win32_error(ret));
492 if (strcmp(cfg,
"unregister_elevated") == 0) {
497 if (strstr(cfg,
"clear_prefs") == cfg) {
498 cfg += strlen(
"clear_prefs");
501 if (strstr(cfg,
"child") == cfg) {
503 cfg = strchr(cfg,
':') ? strchr(cfg,
':') + 1 :
"";
507 MSG(INFO,
"See \"-t screen:help\" for usage.\n");
struct audio_frame ** audio
int void(** deleter)(void *))
This file contains definition of some common audio types.
int color_printf(const char *format,...)
void log_msg(int level, const char *format,...)
#define LOG_LEVEL_NOTICE
information that may be interesting
#define LOG_LEVEL_INFO
normal reporting
#define LOG_LEVEL_ERROR
general errors
#define LOG_LEVEL_VERBOSE
#define LOG_LEVEL_WARNING
less severe errors
#define VIDCAP_INIT_NOERR
state not initialized, other action performed (typically help)
#define VIDEO_CAPTURE_ABI_VERSION
#define VIDCAP_INIT_FAIL
error ocured
@ LIBRARY_CLASS_VIDEO_CAPTURE
#define REGISTER_MODULE(name, info, lclass, abi)
Registers module to global modules' registry.
#define IS_KEY_PREFIX(tok, key)
expands to true value if <k> from tok in format <k>=<v> is prefix of key
#define IF_NOT_NULL_ELSE(val, default_val)
static int run_child_process()
static struct video_frame * vidcap_screen_win_grab(void *state, struct audio_frame **audio)
const struct video_capture_info vidcap_dshow_info
static void vidcap_screen_win_probe(struct device_info **available_cards, int *count, void(**deleter)(void *))
static bool is_library_registered()
static void print_winreg_values()
HRESULT __stdcall(* func)()
static bool delete_winreg_tree()
static LRESULT set_winreg_key(const char *key, int val)
#define CHECK_NOT_NULL(cmd, err_action)
static int vidcap_screen_win_init(struct vidcap_params *params, void **state)
static void vidcap_screen_win_done(void *state)
#define FILTER_UPSTREAM_URL
static bool vidcap_screen_win_process_params(const char *fmt)
static const struct video_capture_info vidcap_screen_win_info
static bool get_if_supported_key_val(char *fmt, const char **key, const char **val_c)
static bool set_key_from_str(const char *key, const char *val_c)
#define SCREEN_CAP_REG_TREE
#define REG_FRIENDLY_NAME
static int register_screen_cap_rec_library(bool is_elevated)
static bool unregister_filter()
static int load_screen_cap_rec_library(struct vidcap_screen_win_state *s)
static const char *const screen_cap_rec_opts[]
struct used to comunicate with audio capture/playback device
bool filter_registered_here
we were able to register filter as a normal user -> cleanup at end
int(* init)(struct vidcap_params *param, void **state)
void(* done)(void *state)
struct video_frame *(* grab)(void *state, struct audio_frame **audio)
Struct video_frame represents a video frame and contains video description.
char * wrap_paragraph(char *text)
This is an umbrella header for video functions.
struct module * vidcap_params_get_parent(const struct vidcap_params *params)
void vidcap_params_set_parent(struct vidcap_params *params, struct module *mod)
void vidcap_params_free_struct(struct vidcap_params *buf)
const char * vidcap_params_get_fmt(const struct vidcap_params *params)
struct vidcap_params * vidcap_params_allocate(void)
void vidcap_params_set_device(struct vidcap_params *params, const char *config)