Skip to content

Commit

Permalink
Support capture/exclude multiple
Browse files Browse the repository at this point in the history
  • Loading branch information
bozbez committed Feb 19, 2022
1 parent ac2da3f commit 22223c3
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 57 deletions.
174 changes: 124 additions & 50 deletions src/audio-capture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ void AudioCapture::WorkerUpdate()

std::set<DWORD> pids;
for (auto &[key, executable] : sessions) {
if ((config.executable != executable) ^ config.exclude)
if ((!config.executables.contains(executable)) ^ config.exclude)
continue;

pids.insert(key.pid);
Expand All @@ -90,7 +90,6 @@ void AudioCapture::WorkerUpdate()
return;
}

debug("target not found: %s", config.executable.value().c_str());
StopCapture();
}

Expand Down Expand Up @@ -196,13 +195,11 @@ void AudioCapture::Update(obs_data_t *settings)
.exclude = obs_data_get_bool(settings, SETTING_EXCLUDE),
};

if (new_config.mode == MODE_SESSION) {
const char *val = obs_data_get_string(settings, SETTING_SESSION);
new_config.executable = std::string(val);
}
if (new_config.mode == MODE_SESSION)
new_config.executables = GetExecutables(settings);

auto lock = config_section.lock();
config = new_config;
config = std::move(new_config);
lock.reset();

PostThreadMessageA(worker_tid, CaptureEvents::Update, NULL, NULL);
Expand Down Expand Up @@ -346,7 +343,10 @@ static bool mode_callback(obs_properties_t *ps, obs_property_t *p, obs_data_t *s
{
int mode = obs_data_get_int(settings, SETTING_MODE);

p = obs_properties_get(ps, SETTING_SESSION);
p = obs_properties_get(ps, SETTING_EXECUTABLE_LIST);
obs_property_set_visible(p, mode == MODE_SESSION);

p = obs_properties_get(ps, SETTING_ACTIVE_SESSION_GROUP);
obs_property_set_visible(p, mode == MODE_SESSION);

p = obs_properties_get(ps, SETTING_EXCLUDE);
Expand Down Expand Up @@ -374,82 +374,156 @@ AudioCapture::MakeSessionOptionStrings(std::set<DWORD> pids, const std::string &
return {std::format("[{}] {}", pids_string, executable), executable};
}

static bool session_callback(obs_properties_t *ps, obs_property_t *p, obs_data_t *settings)
static bool executable_list_callback(void *data, obs_properties_t *ps, obs_property_t *p,
obs_data_t *settings)
{
bool match = false;
size_t i = 0;
auto *ctx = static_cast<AudioCapture *>(data);

const char *cur_val = obs_data_get_string(settings, SETTING_SESSION);
if (!cur_val)
return false;
auto *active_session_list = obs_properties_get(ps, SETTING_ACTIVE_SESSION_LIST);
auto *active_session_add = obs_properties_get(ps, SETTING_ACTIVE_SESSION_ADD);

for (;;) {
const char *val = obs_property_list_item_string(p, i++);
if (!val)
break;
obs_property_list_clear(active_session_list);
ctx->FillActiveSessionList(active_session_list, active_session_add);

if (strcmp(val, cur_val) == 0) {
match = true;
break;
}
}
return true;
}

if (cur_val && *cur_val && !match) {
auto [name, val] = AudioCapture::MakeSessionOptionStrings({}, std::string(cur_val));
static bool session_add_callback(obs_properties_t *ps, obs_property_t *p, void *data)
{
auto *ctx = static_cast<AudioCapture *>(data);
auto *source = ctx->GetSource();

obs_property_list_insert_string(p, 1, name.c_str(), val.c_str());
obs_property_list_item_disable(p, 1, true);
return true;
auto *settings = obs_source_get_settings(source);
auto *executable_list_array = obs_data_get_array(settings, SETTING_EXECUTABLE_LIST);

if (obs_data_array_count(executable_list_array) == 0) {
obs_data_array_release(executable_list_array);

executable_list_array = obs_data_array_create();
obs_data_set_array(settings, SETTING_EXECUTABLE_LIST, executable_list_array);
}

return false;
const char *executable = obs_data_get_string(settings, SETTING_ACTIVE_SESSION_LIST);
auto *executable_obj = obs_data_create();

obs_data_set_bool(executable_obj, "hidden", false);
obs_data_set_bool(executable_obj, "selected", false);
obs_data_set_string(executable_obj, "value", executable);

obs_data_array_push_back(executable_list_array, executable_obj);

auto *active_session_list = obs_properties_get(ps, SETTING_ACTIVE_SESSION_LIST);
auto *active_session_add = obs_properties_get(ps, SETTING_ACTIVE_SESSION_ADD);

obs_property_list_clear(active_session_list);
ctx->FillActiveSessionList(active_session_list, active_session_add);

obs_data_release(executable_obj);
obs_data_array_release(executable_list_array);
obs_data_release(settings);

return true;
}

static obs_properties_t *audio_capture_properties(void *data)
std::set<std::string> AudioCapture::GetExecutables(obs_data_t *settings)
{
auto *ctx = static_cast<AudioCapture *>(data);

obs_properties_t *ps = obs_properties_create();
obs_property_t *p;
auto *executable_list_array = obs_data_get_array(settings, SETTING_EXECUTABLE_LIST);
auto count = obs_data_array_count(executable_list_array);

// Mode setting (specific session or hotkey)
p = obs_properties_add_list(ps, SETTING_MODE, TEXT_MODE, OBS_COMBO_TYPE_LIST,
OBS_COMBO_FORMAT_INT);
std::set<std::string> executables;

obs_property_list_add_int(p, TEXT_MODE_SESSION, MODE_SESSION);
obs_property_list_add_int(p, TEXT_MODE_HOTKEY, MODE_HOTKEY);
for (std::size_t i = 0; i < count; ++i) {
auto *item = obs_data_array_item(executable_list_array, i);
auto *executable = obs_data_get_string(item, "value");

obs_property_set_modified_callback(p, mode_callback);
executables.insert(std::string(executable));
}

// Session setting
p = obs_properties_add_list(ps, SETTING_SESSION, TEXT_SESSION, OBS_COMBO_TYPE_LIST,
OBS_COMBO_FORMAT_STRING);
obs_data_array_release(executable_list_array);
return executables;
}

obs_property_list_add_string(p, "", "");
void AudioCapture::FillActiveSessionList(obs_property_t *session_list, obs_property_t *session_add)
{
auto *settings = obs_source_get_settings(GetSource());

auto sessions = ctx->GetSessions();
auto sessions = GetSessions();
auto executables = GetExecutables(settings);

std::unordered_map<std::string, std::set<DWORD>> session_options;
for (auto &[key, executable] : sessions)
for (auto &[key, executable] : sessions) {
if (executables.contains(executable))
continue;

session_options[executable].insert(key.pid);
}

for (auto &[executable, pids] : session_options) {
auto [name, val] = AudioCapture::MakeSessionOptionStrings(pids, executable);
obs_property_list_add_string(p, name.c_str(), val.c_str());
obs_property_list_add_string(session_list, name.c_str(), val.c_str());
}

obs_property_set_modified_callback(p, session_callback);
obs_property_set_enabled(session_add, session_options.size() != 0);
obs_property_set_enabled(session_list, session_options.size() != 0);

obs_data_release(settings);
}

static obs_properties_t *audio_capture_properties(void *data)
{
auto *ctx = static_cast<AudioCapture *>(data);

obs_properties_t *ps = obs_properties_create();

// Mode setting (specific session or hotkey)
auto *mode = obs_properties_add_list(ps, SETTING_MODE, TEXT_MODE, OBS_COMBO_TYPE_LIST,
OBS_COMBO_FORMAT_INT);

obs_property_list_add_int(mode, TEXT_MODE_SESSION, MODE_SESSION);
obs_property_list_add_int(mode, TEXT_MODE_HOTKEY, MODE_HOTKEY);

obs_property_set_modified_callback(mode, mode_callback);

// Executable list setting
auto *executable_list =
obs_properties_add_editable_list(ps, SETTING_EXECUTABLE_LIST, TEXT_EXECUTABLE_LIST,
OBS_EDITABLE_LIST_TYPE_STRINGS, NULL, NULL);

obs_property_set_modified_callback2(executable_list, executable_list_callback, ctx);

// Exclude setting
p = obs_properties_add_bool(ps, SETTING_EXCLUDE, TEXT_EXCLUDE);
obs_properties_add_bool(ps, SETTING_EXCLUDE, TEXT_EXCLUDE);

// Active session group
obs_properties_t *active_session_group = obs_properties_create();

// Active session list
auto *active_session_list = obs_properties_add_list(
active_session_group, SETTING_ACTIVE_SESSION_LIST, TEXT_ACTIVE_SESSION_LIST,
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING);

// Add session button
auto *active_session_add =
obs_properties_add_button(active_session_group, SETTING_ACTIVE_SESSION_ADD,
TEXT_ACTIVE_SESSION_ADD, session_add_callback);

ctx->FillActiveSessionList(active_session_list, active_session_add);

// Active session group
obs_properties_add_group(ps, SETTING_ACTIVE_SESSION_GROUP, TEXT_ACTIVE_SESSION_GROUP,
OBS_GROUP_NORMAL, active_session_group);

return ps;
}

static void audio_capture_defaults(obs_data_t *settings)
{
obs_data_set_default_int(settings, SETTING_MODE, MODE_SESSION);
obs_data_set_default_string(settings, SETTING_SESSION, "");

auto *executable_list = obs_data_array_create();
obs_data_set_default_array(settings, SETTING_EXECUTABLE_LIST, executable_list);
obs_data_array_release(executable_list);

obs_data_set_default_bool(settings, SETTING_EXCLUDE, false);
}

Expand Down
23 changes: 20 additions & 3 deletions src/audio-capture.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <cstdio>
#include <optional>
#include <tuple>
#include <set>

#include <windows.h>
#include <wil/resource.h>
Expand All @@ -17,7 +18,13 @@
/* clang-format off */

#define SETTING_MODE "mode"
#define SETTING_SESSION "session"

#define SETTING_EXECUTABLE_LIST "executable_list"

#define SETTING_ACTIVE_SESSION_GROUP "active_session_group"
#define SETTING_ACTIVE_SESSION_LIST "active_session_list"
#define SETTING_ACTIVE_SESSION_ADD "active_session_add"

#define SETTING_EXCLUDE "exclude"

#define TEXT_NAME obs_module_text("Name")
Expand All @@ -26,7 +33,12 @@
#define TEXT_MODE_SESSION obs_module_text("Mode.Session")
#define TEXT_MODE_HOTKEY obs_module_text("Mode.Hotkey")

#define TEXT_SESSION obs_module_text("Session")
#define TEXT_EXECUTABLE_LIST obs_module_text("ExecutableList")

#define TEXT_ACTIVE_SESSION_GROUP obs_module_text("ActiveSession.Group")
#define TEXT_ACTIVE_SESSION_LIST obs_module_text("ActiveSession.List")
#define TEXT_ACTIVE_SESSION_ADD obs_module_text("ActiveSession.Add")

#define TEXT_EXCLUDE obs_module_text("Exclude")

#define TEXT_HOTKEY_START obs_module_text("Hotkey.Start")
Expand All @@ -46,7 +58,7 @@ enum mode { MODE_SESSION, MODE_HOTKEY };
struct AudioCaptureConfig {
enum mode mode = MODE_SESSION;

std::optional<std::string> executable;
std::set<std::string> executables;
HWND hotkey_window = NULL;

bool exclude = false;
Expand Down Expand Up @@ -85,12 +97,17 @@ class AudioCapture {
void Run();

public:
obs_source_t *GetSource() { return source; }

void Update(obs_data_t *settings);
std::unordered_map<SessionKey, std::string> GetSessions();

static std::tuple<std::string, std::string>
MakeSessionOptionStrings(std::set<DWORD> pids, const std::string &executable);

void FillActiveSessionList(obs_property_t *session_list, obs_property_t *session_add);
std::set<std::string> GetExecutables(obs_data_t *settings);

bool IsUwpWindow(HWND window);
HWND GetUwpActualWindow(HWND parent_window);

Expand Down
6 changes: 2 additions & 4 deletions src/session-monitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,15 +76,13 @@ SessionWatcher::SessionWatcher(DWORD worker_tid,
WideCharToMultiByte(CP_UTF8, 0, name_buf, -1, &executable_path[0], num_chars, NULL, NULL);

executable = executable_path.substr(executable_path.find_last_of("\\") + 1);

// debug("registered new session: [%d] %s", pid, executable.c_str());
debug("registered new session: [%d] %s", pid, executable.c_str());
}

SessionWatcher::~SessionWatcher()
{
session_control->UnregisterAudioSessionNotification(&notification_client.value());

// debug("session expired: [%d] %s", pid, executable.c_str());
debug("session expired: [%d] %s", pid, executable.c_str());
}

void SessionMonitor::Init()
Expand Down

0 comments on commit 22223c3

Please sign in to comment.