Add application audio option

This commit is contained in:
dec05eba
2024-11-13 22:18:30 +01:00
parent 590428425e
commit 4ba1e814b7
20 changed files with 441 additions and 137 deletions

View File

@@ -34,3 +34,8 @@ This software is licensed under GPL3.0-only. Files under `fonts/` directory are
# Screenshots
![](https://dec05eba.com/images/gsr-overlay-screenshot-front.webp)
![](https://dec05eba.com/images/gsr-overlay-screenshot-settings.webp)
# Donations
If you want to donate you can donate via bitcoin or monero.
* Bitcoin: bc1qqvuqnwrdyppf707ge27fqz2n9y9gu7lf5ypyuf
* Monero: 4An9kp2qW1C9Gah7ewv4JzcNFQ5TAX7ineGCqXWK6vQnhsGGcRpNgcn8r9EC3tMcgY7vqCKs3nSRXhejMHBaGvFdN2egYet

27
TODO
View File

@@ -52,3 +52,30 @@ Add profiles and hotkey to switch between profiles (show notification when switc
Fix first frame being black.
Add support for systray.
Add option to take screenshot.
Move event callbacks to a global list instead of std::function object in each widget. This reduces the size of widgets,
since most widgets wont have the event callback set.
This event callback would pass the widget as an argument.
This could be done transparently by having a function in the widget to set a callback function
and that could add a wrapper that checks if the callback received _this_, and that would call the callback set by the user.
Make save-video-in-game-folder.sh and notify-saved-name.sh run ~/.config/gpu-screen-recorder/on-save.sh if it exists.
This could for example be used to automatically convert the video to a different format or to upload it to a server.
Before this script is called gsr-ui should define an environment variable GSR_PROFILE that specifies the name of the profile.
This profile name can be used to decide which action the users script should do, for example to convert the video to a 4chan friendly webm file
if the profile is called 4chan.
Create a directory of such example scripts, including 4chan webm one.
On nvidia check if suspend fix is applied. If not, show a popup asking the user to apply it (and apply it automatically).
Show warning when using steam deck or when trying to capture hevc/av1 on amd (the same warnings as gpu screen recorder gtk).
Add option to capture application audio. This should show a popup where you can use one of the available applications or a custom one and choose to record that application or all applications except that one.
Add profile option. Convert view to profile, add an option at the bottom that says "Edit profiles..." which should show a popup where you can create/remove profiles. New profiles should always be in advanced view.
Verify monitor/audio when starting recording. Give an error if the options are no longer valid.
Add option to record focused monitor. This is less error prone when plugging in monitors, etc.

View File

@@ -23,8 +23,11 @@ namespace gsr {
int32_t fps = 60;
int32_t video_bitrate = 15000;
bool merge_audio_tracks = true;
bool application_audio_invert = false;
bool change_video_resolution = false;
std::string audio_type_view = "audio_devices";
std::vector<std::string> audio_tracks;
std::vector<std::string> application_audio;
std::string color_range = "limited";
std::string video_quality = "very_high";
std::string video_codec = "auto";

View File

@@ -76,4 +76,5 @@ namespace gsr {
GsrInfoExitStatus get_gpu_screen_recorder_info(GsrInfo *gsr_info);
std::vector<AudioDevice> get_audio_devices();
std::vector<std::string> get_application_audio();
}

View File

@@ -90,14 +90,13 @@ namespace gsr {
std::string resources_path;
GsrInfo gsr_info;
egl_functions egl_funcs;
std::vector<gsr::AudioDevice> audio_devices;
mgl::Texture window_texture_texture;
mgl::Sprite window_texture_sprite;
mgl::Texture screenshot_texture;
mgl::Sprite screenshot_sprite;
mgl::Rectangle bg_screenshot_overlay;
WindowTexture window_texture;
gsr::PageStack page_stack;
PageStack page_stack;
mgl::Rectangle top_bar_background;
mgl::Text top_bar_text;
mgl::Sprite logo_sprite;

View File

@@ -19,6 +19,7 @@ namespace gsr {
mgl::vec2f get_size() override;
void set_text(std::string str);
const std::string& get_text() const;
private:
mgl::Text text;
};

View File

@@ -0,0 +1,24 @@
#pragma once
#include "Widget.hpp"
namespace gsr {
class LineSeparator : public Widget {
public:
enum class Type {
HORIZONTAL
};
LineSeparator(Type type, float width);
LineSeparator(const LineSeparator&) = delete;
LineSeparator& operator=(const LineSeparator&) = delete;
bool on_event(mgl::Event &event, mgl::Window &window, mgl::vec2f offset) override;
void draw(mgl::Window &window, mgl::vec2f offset) override;
mgl::vec2f get_size() override;
private:
Type type;
float width;
};
}

View File

@@ -24,7 +24,7 @@ namespace gsr {
STREAM
};
SettingsPage(Type type, const GsrInfo &gsr_info, std::vector<AudioDevice> audio_devices, Config &config, PageStack *page_stack);
SettingsPage(Type type, const GsrInfo &gsr_info, Config &config, PageStack *page_stack);
SettingsPage(const SettingsPage&) = delete;
SettingsPage& operator=(const SettingsPage&) = delete;
@@ -48,13 +48,23 @@ namespace gsr {
std::unique_ptr<List> create_restore_portal_session_section();
std::unique_ptr<Widget> create_change_video_resolution_section();
std::unique_ptr<Widget> create_capture_target(const GsrInfo &gsr_info);
std::unique_ptr<ComboBox> create_audio_track_selection_checkbox();
std::unique_ptr<Button> create_remove_audio_track_button(List *audio_device_list_ptr);
std::unique_ptr<List> create_audio_track();
std::unique_ptr<Button> create_add_audio_track_button();
std::unique_ptr<List> create_audio_track_section();
std::unique_ptr<ComboBox> create_audio_device_selection_combobox();
std::unique_ptr<Button> create_remove_audio_device_button(List *audio_device_list_ptr);
std::unique_ptr<List> create_audio_device();
std::unique_ptr<Button> create_add_audio_device_button();
std::unique_ptr<List> create_audio_device_track_section();
std::unique_ptr<CheckBox> create_merge_audio_tracks_checkbox();
std::unique_ptr<RadioButton> create_audio_type_button();
std::unique_ptr<Widget> create_audio_device_section();
std::unique_ptr<List> create_application_audio_track_section();
std::unique_ptr<CheckBox> create_application_audio_invert_checkbox();
std::unique_ptr<List> create_application_audio_section();
std::unique_ptr<ComboBox> create_application_audio_selection_combobox();
std::unique_ptr<Button> create_remove_application_audio_button(List *app_audio_item);
std::unique_ptr<List> create_application_audio();
std::unique_ptr<List> create_custom_application_audio();
std::unique_ptr<List> create_add_application_audio_buttons();
std::unique_ptr<Widget> create_audio_section();
std::unique_ptr<List> create_video_quality_box();
std::unique_ptr<Entry> create_video_bitrate_entry();
std::unique_ptr<List> create_video_bitrate();
@@ -98,7 +108,8 @@ namespace gsr {
std::unique_ptr<List> create_stream_container_section();
void add_stream_widgets();
void load_audio_tracks(RecordOptions &record_options);
void load_audio_device_tracks(RecordOptions &record_options);
void load_application_audio_tracks(RecordOptions &record_options);
void load_common(RecordOptions &record_options);
void load_replay();
void load_record();
@@ -112,6 +123,7 @@ namespace gsr {
Type type;
Config &config;
std::vector<AudioDevice> audio_devices;
std::vector<std::string> application_audio;
GsrPage *content_page_ptr = nullptr;
ScrollablePage *settings_scrollable_page_ptr = nullptr;
@@ -133,7 +145,12 @@ namespace gsr {
Entry *video_bitrate_entry_ptr = nullptr;
List *video_bitrate_list_ptr = nullptr;
List *audio_devices_list_ptr = nullptr;
List *audio_devices_section_list_ptr = nullptr;
List *application_audio_section_list_ptr = nullptr;
CheckBox *merge_audio_tracks_checkbox_ptr = nullptr;
RadioButton *audio_type_radio_button_ptr = nullptr;
List *application_audio_list_ptr = nullptr;
CheckBox *application_audio_invert_checkbox_ptr = nullptr;
CheckBox *change_video_resolution_checkbox_ptr = nullptr;
ComboBox *color_range_box_ptr = nullptr;
ComboBox *video_quality_box_ptr = nullptr;

View File

@@ -16,6 +16,7 @@ namespace gsr {
void draw(mgl::Window &window, mgl::vec2f offset) override;
mgl::vec2f get_size() override;
mgl::vec2f get_inner_size() override;
private:
Label label;
std::unique_ptr<Widget> inner_widget;

View File

@@ -23,6 +23,7 @@ src = [
'src/gui/Utils.cpp',
'src/gui/DropdownButton.cpp',
'src/gui/Label.cpp',
'src/gui/LineSeparator.cpp',
'src/gui/CustomRendererWidget.cpp',
'src/gui/FileChooser.cpp',
'src/gui/SettingsPage.cpp',

View File

@@ -6,7 +6,6 @@ filepath="$1"
type="$2"
file_name="$(basename "$filepath")"
file_dir="$(dirname "$filepath")"
case "$type" in
"regular")

View File

@@ -57,8 +57,11 @@ namespace gsr {
{"streaming.record_options.fps", &config.streaming_config.record_options.fps},
{"streaming.record_options.video_bitrate", &config.streaming_config.record_options.video_bitrate},
{"streaming.record_options.merge_audio_tracks", &config.streaming_config.record_options.merge_audio_tracks},
{"streaming.record_options.application_audio_invert", &config.streaming_config.record_options.application_audio_invert},
{"streaming.record_options.change_video_resolution", &config.streaming_config.record_options.change_video_resolution},
{"streaming.record_options.audio_type_view", &config.streaming_config.record_options.audio_type_view},
{"streaming.record_options.audio_track", &config.streaming_config.record_options.audio_tracks},
{"streaming.record_options.application_audio", &config.streaming_config.record_options.application_audio},
{"streaming.record_options.color_range", &config.streaming_config.record_options.color_range},
{"streaming.record_options.video_quality", &config.streaming_config.record_options.video_quality},
{"streaming.record_options.codec", &config.streaming_config.record_options.video_codec},
@@ -85,8 +88,11 @@ namespace gsr {
{"record.record_options.fps", &config.record_config.record_options.fps},
{"record.record_options.video_bitrate", &config.record_config.record_options.video_bitrate},
{"record.record_options.merge_audio_tracks", &config.record_config.record_options.merge_audio_tracks},
{"record.record_options.application_audio_invert", &config.record_config.record_options.application_audio_invert},
{"record.record_options.change_video_resolution", &config.record_config.record_options.change_video_resolution},
{"record.record_options.audio_type_view", &config.record_config.record_options.audio_type_view},
{"record.record_options.audio_track", &config.record_config.record_options.audio_tracks},
{"record.record_options.application_audio", &config.record_config.record_options.application_audio},
{"record.record_options.color_range", &config.record_config.record_options.color_range},
{"record.record_options.video_quality", &config.record_config.record_options.video_quality},
{"record.record_options.codec", &config.record_config.record_options.video_codec},
@@ -112,8 +118,11 @@ namespace gsr {
{"replay.record_options.fps", &config.replay_config.record_options.fps},
{"replay.record_options.video_bitrate", &config.replay_config.record_options.video_bitrate},
{"replay.record_options.merge_audio_tracks", &config.replay_config.record_options.merge_audio_tracks},
{"replay.record_options.application_audio_invert", &config.replay_config.record_options.application_audio_invert},
{"replay.record_options.change_video_resolution", &config.replay_config.record_options.change_video_resolution},
{"replay.record_options.audio_type_view", &config.replay_config.record_options.audio_type_view},
{"replay.record_options.audio_track", &config.replay_config.record_options.audio_tracks},
{"replay.record_options.application_audio", &config.replay_config.record_options.application_audio},
{"replay.record_options.color_range", &config.replay_config.record_options.color_range},
{"replay.record_options.video_quality", &config.replay_config.record_options.video_quality},
{"replay.record_options.codec", &config.replay_config.record_options.video_codec},

View File

@@ -218,4 +218,30 @@ namespace gsr {
return audio_devices;
}
std::vector<std::string> get_application_audio() {
std::vector<std::string> application_audio;
FILE *f = popen("gpu-screen-recorder --list-application-audio", "r");
if(!f) {
fprintf(stderr, "error: 'gpu-screen-recorder --list-application-audio' failed\n");
return application_audio;
}
char output[16384];
ssize_t bytes_read = fread(output, 1, sizeof(output) - 1, f);
if(bytes_read < 0 || ferror(f)) {
fprintf(stderr, "error: failed to read 'gpu-screen-recorder --list-application-audio' output\n");
pclose(f);
return application_audio;
}
output[bytes_read] = '\0';
string_split_char({output, (size_t)bytes_read}, '\n', [&](std::string_view line) {
application_audio.emplace_back(line);
return true;
});
return application_audio;
}
}

View File

@@ -130,6 +130,15 @@ namespace gsr {
&& diff_int(geometry.width, monitor->size.x, margin) && diff_int(geometry.height, monitor->size.y, margin);
}
static void set_focused_window(Display *dpy, Window window) {
XSetInputFocus(dpy, window, RevertToParent, CurrentTime);
const Atom net_active_window_atom = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
XChangeProperty(dpy, DefaultRootWindow(dpy), net_active_window_atom, XA_WINDOW, 32, PropModeReplace, (const unsigned char*)&window, 1);
XFlush(dpy);
}
#define _NET_WM_STATE_REMOVE 0
#define _NET_WM_STATE_ADD 1
#define _NET_WM_STATE_TOGGLE 2
@@ -370,8 +379,6 @@ namespace gsr {
update_compositor_texture(focused_monitor);
audio_devices = get_audio_devices();
top_bar_text = mgl::Text("GPU Screen Recorder", get_theme().top_bar_font);
logo_sprite = mgl::Sprite(&get_theme().logo_texture);
@@ -418,7 +425,7 @@ namespace gsr {
button->set_item_icon("save", &get_theme().save_texture);
button->on_click = [this](const std::string &id) {
if(id == "settings") {
auto replay_settings_page = std::make_unique<SettingsPage>(SettingsPage::Type::REPLAY, gsr_info, audio_devices, config, &page_stack);
auto replay_settings_page = std::make_unique<SettingsPage>(SettingsPage::Type::REPLAY, gsr_info, config, &page_stack);
page_stack.push(std::move(replay_settings_page));
} else if(id == "save") {
on_press_save_replay();
@@ -439,7 +446,7 @@ namespace gsr {
button->set_item_icon("pause", &get_theme().pause_texture);
button->on_click = [this](const std::string &id) {
if(id == "settings") {
auto record_settings_page = std::make_unique<SettingsPage>(SettingsPage::Type::RECORD, gsr_info, audio_devices, config, &page_stack);
auto record_settings_page = std::make_unique<SettingsPage>(SettingsPage::Type::RECORD, gsr_info, config, &page_stack);
page_stack.push(std::move(record_settings_page));
} else if(id == "pause") {
toggle_pause();
@@ -458,7 +465,7 @@ namespace gsr {
button->set_item_icon("start", &get_theme().play_texture);
button->on_click = [this](const std::string &id) {
if(id == "settings") {
auto stream_settings_page = std::make_unique<SettingsPage>(SettingsPage::Type::STREAM, gsr_info, audio_devices, config, &page_stack);
auto stream_settings_page = std::make_unique<SettingsPage>(SettingsPage::Type::STREAM, gsr_info, config, &page_stack);
page_stack.push(std::move(stream_settings_page));
} else if(id == "start") {
on_press_start_stream();
@@ -521,7 +528,7 @@ namespace gsr {
// TODO: This breaks global hotkeys
//XGrabKeyboard(display, window->get_system_handle(), True, GrabModeAsync, GrabModeAsync, CurrentTime);
XSetInputFocus(display, window->get_system_handle(), RevertToPointerRoot, CurrentTime);
set_focused_window(display, window->get_system_handle());
XFlush(display);
//window->set_fullscreen(true);
@@ -864,6 +871,46 @@ namespace gsr {
kill(gpu_screen_recorder_process, SIGUSR1);
}
static void add_common_gpu_screen_recorder_args(RecordOptions &record_options, std::vector<const char*> &args, const std::string &video_bitrate, const char *region, const std::string &audio_devices_merged, const std::string &application_audio_merged) {
if(record_options.video_quality == "custom") {
args.push_back("-bm");
args.push_back("cbr");
args.push_back("-q");
args.push_back(video_bitrate.c_str());
} else {
args.push_back("-q");
args.push_back(record_options.video_quality.c_str());
}
if(record_options.record_area_option == "focused" || record_options.change_video_resolution) {
args.push_back("-s");
args.push_back(region);
}
if(record_options.audio_type_view == "audio_devices") {
if(record_options.merge_audio_tracks) {
args.push_back("-a");
args.push_back(audio_devices_merged.c_str());
} else {
for(const std::string &audio_track : record_options.audio_tracks) {
args.push_back("-a");
args.push_back(audio_track.c_str());
}
}
} else if(record_options.audio_type_view == "app_audio") {
const char *app_audio_option = record_options.application_audio_invert ? "-aai" : "-aa";
if(record_options.merge_audio_tracks) {
args.push_back(app_audio_option);
args.push_back(application_audio_merged.c_str());
} else {
for(const std::string &app_audio : record_options.application_audio) {
args.push_back(app_audio_option);
args.push_back(app_audio.c_str());
}
}
}
}
void Overlay::on_press_start_replay(bool disable_notification) {
switch(recording_status) {
case RecordingStatus::NONE:
@@ -900,13 +947,12 @@ namespace gsr {
return;
}
audio_devices = get_audio_devices();
// TODO: Validate input, fallback to valid values
const std::string fps = std::to_string(config.replay_config.record_options.fps);
const std::string video_bitrate = std::to_string(config.replay_config.record_options.video_bitrate);
const std::string output_directory = config.replay_config.save_directory;
const std::string audio_tracks_merged = merge_audio_tracks(config.replay_config.record_options.audio_tracks);
const std::string audio_devices_merged = merge_audio_tracks(config.replay_config.record_options.audio_tracks);
const std::string application_audio_merged = merge_audio_tracks(config.replay_config.record_options.application_audio);
const std::string framerate_mode = config.replay_config.record_options.framerate_mode == "auto" ? "vfr" : config.replay_config.record_options.framerate_mode;
const std::string replay_time = std::to_string(config.replay_config.replay_time);
const char *video_codec = config.replay_config.record_options.video_codec.c_str();
@@ -937,30 +983,7 @@ namespace gsr {
"-o", output_directory.c_str()
};
if(config.replay_config.record_options.video_quality == "custom") {
args.push_back("-bm");
args.push_back("cbr");
args.push_back("-q");
args.push_back(video_bitrate.c_str());
} else {
args.push_back("-q");
args.push_back(config.replay_config.record_options.video_quality.c_str());
}
if(config.replay_config.record_options.record_area_option == "focused" || config.replay_config.record_options.change_video_resolution) {
args.push_back("-s");
args.push_back(region);
}
if(config.replay_config.record_options.merge_audio_tracks) {
args.push_back("-a");
args.push_back(audio_tracks_merged.c_str());
} else {
for(const std::string &audio_track : config.replay_config.record_options.audio_tracks) {
args.push_back("-a");
args.push_back(audio_track.c_str());
}
}
add_common_gpu_screen_recorder_args(config.replay_config.record_options, args, video_bitrate, region, audio_devices_merged, application_audio_merged);
setenv("GSR_SHOW_SAVED_NOTIFICATION", config.replay_config.show_replay_saved_notifications ? "1" : "0", true);
const std::string script_to_run_on_save = resources_path + (config.replay_config.save_video_in_game_folder ? "scripts/save-video-in-game-folder.sh" : "scripts/notify-saved-name.sh");
@@ -1025,13 +1048,12 @@ namespace gsr {
return;
}
audio_devices = get_audio_devices();
// TODO: Validate input, fallback to valid values
const std::string fps = std::to_string(config.record_config.record_options.fps);
const std::string video_bitrate = std::to_string(config.record_config.record_options.video_bitrate);
const std::string output_file = config.record_config.save_directory + "/Video_" + get_date_str() + "." + container_to_file_extension(config.record_config.container.c_str());
const std::string audio_tracks_merged = merge_audio_tracks(config.record_config.record_options.audio_tracks);
const std::string audio_devices_merged = merge_audio_tracks(config.record_config.record_options.audio_tracks);
const std::string application_audio_merged = merge_audio_tracks(config.record_config.record_options.application_audio);
const std::string framerate_mode = config.record_config.record_options.framerate_mode == "auto" ? "vfr" : config.record_config.record_options.framerate_mode;
const char *video_codec = config.record_config.record_options.video_codec.c_str();
const char *encoder = "gpu";
@@ -1060,30 +1082,7 @@ namespace gsr {
"-o", output_file.c_str()
};
if(config.record_config.record_options.video_quality == "custom") {
args.push_back("-bm");
args.push_back("cbr");
args.push_back("-q");
args.push_back(video_bitrate.c_str());
} else {
args.push_back("-q");
args.push_back(config.record_config.record_options.video_quality.c_str());
}
if(config.record_config.record_options.record_area_option == "focused" || config.record_config.record_options.change_video_resolution) {
args.push_back("-s");
args.push_back(region);
}
if(config.record_config.record_options.merge_audio_tracks) {
args.push_back("-a");
args.push_back(audio_tracks_merged.c_str());
} else {
for(const std::string &audio_track : config.record_config.record_options.audio_tracks) {
args.push_back("-a");
args.push_back(audio_track.c_str());
}
}
add_common_gpu_screen_recorder_args(config.record_config.record_options, args, video_bitrate, region, audio_devices_merged, application_audio_merged);
setenv("GSR_SHOW_SAVED_NOTIFICATION", config.record_config.show_video_saved_notifications ? "1" : "0", true);
const std::string script_to_run_on_save = resources_path + (config.record_config.save_video_in_game_folder ? "scripts/save-video-in-game-folder.sh" : "scripts/notify-saved-name.sh");
@@ -1178,12 +1177,11 @@ namespace gsr {
return;
}
audio_devices = get_audio_devices();
// TODO: Validate input, fallback to valid values
const std::string fps = std::to_string(config.streaming_config.record_options.fps);
const std::string video_bitrate = std::to_string(config.streaming_config.record_options.video_bitrate);
const std::string audio_tracks_merged = merge_audio_tracks(config.streaming_config.record_options.audio_tracks);
const std::string audio_devices_merged = merge_audio_tracks(config.streaming_config.record_options.audio_tracks);
const std::string application_audio_merged = merge_audio_tracks(config.streaming_config.record_options.application_audio);
const std::string framerate_mode = config.streaming_config.record_options.framerate_mode == "auto" ? "vfr" : config.streaming_config.record_options.framerate_mode;
const char *video_codec = config.streaming_config.record_options.video_codec.c_str();
const char *encoder = "gpu";
@@ -1218,30 +1216,7 @@ namespace gsr {
"-o", url.c_str()
};
if(config.streaming_config.record_options.video_quality == "custom") {
args.push_back("-bm");
args.push_back("cbr");
args.push_back("-q");
args.push_back(video_bitrate.c_str());
} else {
args.push_back("-q");
args.push_back(config.streaming_config.record_options.video_quality.c_str());
}
if(config.streaming_config.record_options.record_area_option == "focused" || config.streaming_config.record_options.change_video_resolution) {
args.push_back("-s");
args.push_back(region);
}
if(config.streaming_config.record_options.merge_audio_tracks) {
args.push_back("-a");
args.push_back(audio_tracks_merged.c_str());
} else {
for(const std::string &audio_track : config.streaming_config.record_options.audio_tracks) {
args.push_back("-a");
args.push_back(audio_track.c_str());
}
}
add_common_gpu_screen_recorder_args(config.streaming_config.record_options, args, video_bitrate, region, audio_devices_merged, application_audio_merged);
args.push_back(nullptr);

View File

@@ -10,7 +10,7 @@ namespace gsr {
static const float spacing_scale = 0.005f;
static const float check_animation_speed = 10.0f;
static mgl::Color color_multiply(mgl::Color color, float multiply) {
static mgl::Color color_multiply_ignore_alpha(mgl::Color color, float multiply) {
return mgl::Color(color.r * multiply, color.g * multiply, color.b * multiply, color.a);
}
@@ -18,12 +18,12 @@ namespace gsr {
return source + (destination - source) * interpolation;
}
static mgl::Color interpolate_color(mgl::Color source, mgl::Color destination, float interpolation) {
static mgl::Color interpolate_color_ignore_alpha(mgl::Color source, mgl::Color destination, float interpolation) {
mgl::Color color;
color.r = linear_interpolation(source.r, destination.r, interpolation);
color.g = linear_interpolation(source.g, destination.g, interpolation);
color.b = linear_interpolation(source.b, destination.b, interpolation);
color.a = linear_interpolation(source.a, destination.a, interpolation);
color.a = source.a;
return color;
}
@@ -61,9 +61,9 @@ namespace gsr {
apply_animation();
const mgl::Color background_color_unchecked(0, 0, 0, 120);
const mgl::Color background_color_checked = color_multiply(get_color_theme().tint_color, 0.6f);
background_sprite.set_color(interpolate_color(background_color_unchecked, background_color_checked, toggle_animation_value));
const mgl::Color background_color_unchecked = color_multiply_ignore_alpha(mgl::Color(25, 30, 34), 0.6f);
const mgl::Color background_color_checked = color_multiply_ignore_alpha(get_color_theme().tint_color, 0.6f);
background_sprite.set_color(interpolate_color_ignore_alpha(background_color_unchecked, background_color_checked, toggle_animation_value));
background_sprite.set_position(draw_pos.floor());
window.draw(background_sprite);

View File

@@ -22,6 +22,10 @@ namespace gsr {
text.set_string(std::move(str));
}
const std::string& Label::get_text() const {
return text.get_string();
}
mgl::vec2f Label::get_size() {
if(!visible)
return {0.0f, 0.0f};

45
src/gui/LineSeparator.cpp Normal file
View File

@@ -0,0 +1,45 @@
#include "../../include/gui/LineSeparator.hpp"
#include "../../include/Theme.hpp"
#include <mglpp/window/Window.hpp>
#include <mglpp/graphics/Rectangle.hpp>
namespace gsr {
static const float height_scale = 0.001f;
static mgl::Color color_add_ignore_alpha(mgl::Color color, mgl::Color add) {
return {
(uint8_t)std::min((int)color.r + (int)add.r, 255),
(uint8_t)std::min((int)color.g + (int)add.g, 255),
(uint8_t)std::min((int)color.b + (int)add.b, 255),
color.a
};
}
LineSeparator::LineSeparator(Type type, float width) : type(type), width(width) {
}
bool LineSeparator::on_event(mgl::Event&, mgl::Window&, mgl::vec2f) {
return true;
}
void LineSeparator::draw(mgl::Window &window, mgl::vec2f offset) {
if(!visible)
return;
const mgl::vec2f draw_pos = (position + offset).floor();
const mgl::vec2f size = mgl::vec2f(width, std::max(1.0f, height_scale * get_theme().window_height)).floor();
mgl::Rectangle rectangle(draw_pos, size);
rectangle.set_color(color_add_ignore_alpha(mgl::Color(25, 30, 34), mgl::Color(30, 30, 30)));
window.draw(rectangle);
}
mgl::vec2f LineSeparator::get_size() {
if(!visible)
return {0.0f, 0.0f};
const mgl::vec2f size = mgl::vec2f(width, std::max(1.0f, height_scale * get_theme().window_height)).floor();
return size;
}
}

View File

@@ -1,6 +1,7 @@
#include "../../include/gui/SettingsPage.hpp"
#include "../../include/gui/GsrPage.hpp"
#include "../../include/gui/Label.hpp"
#include "../../include/gui/LineSeparator.hpp"
#include "../../include/gui/PageStack.hpp"
#include "../../include/gui/FileChooser.hpp"
#include "../../include/gui/Subsection.hpp"
@@ -13,15 +14,19 @@
#include <mglpp/graphics/Text.hpp>
#include <mglpp/window/Window.hpp>
#include <string.h>
namespace gsr {
SettingsPage::SettingsPage(Type type, const GsrInfo &gsr_info, std::vector<AudioDevice> audio_devices, Config &config, PageStack *page_stack) :
SettingsPage::SettingsPage(Type type, const GsrInfo &gsr_info, Config &config, PageStack *page_stack) :
StaticPage(mgl::vec2f(get_theme().window_width, get_theme().window_height).floor()),
type(type),
config(config),
audio_devices(std::move(audio_devices)),
page_stack(page_stack),
settings_title_text("Settings", get_theme().title_font)
{
audio_devices = get_audio_devices();
application_audio = get_application_audio();
auto content_page = std::make_unique<GsrPage>();
content_page->add_button("Back", "back", get_color_theme().page_bg_color);
content_page->on_click = [page_stack](const std::string &id) {
@@ -177,7 +182,7 @@ namespace gsr {
return std::make_unique<Subsection>("Record area", std::move(ll), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f));
}
std::unique_ptr<ComboBox> SettingsPage::create_audio_track_selection_checkbox() {
std::unique_ptr<ComboBox> SettingsPage::create_audio_device_selection_combobox() {
auto audio_device_box = std::make_unique<ComboBox>(&get_theme().body_font);
for(const auto &audio_device : audio_devices) {
audio_device_box->add_item(audio_device.description, audio_device.name);
@@ -185,7 +190,7 @@ namespace gsr {
return audio_device_box;
}
std::unique_ptr<Button> SettingsPage::create_remove_audio_track_button(List *audio_device_list_ptr) {
std::unique_ptr<Button> SettingsPage::create_remove_audio_device_button(List *audio_device_list_ptr) {
auto remove_audio_track_button = std::make_unique<Button>(&get_theme().body_font, "Remove", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
remove_audio_track_button->on_click = [this, audio_device_list_ptr]() {
audio_devices_list_ptr->remove_widget(audio_device_list_ptr);
@@ -193,26 +198,25 @@ namespace gsr {
return remove_audio_track_button;
}
std::unique_ptr<List> SettingsPage::create_audio_track() {
std::unique_ptr<List> SettingsPage::create_audio_device() {
auto audio_device_list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
audio_device_list->add_widget(create_audio_track_selection_checkbox());
audio_device_list->add_widget(create_remove_audio_track_button(audio_device_list.get()));
audio_device_list->add_widget(create_audio_device_selection_combobox());
audio_device_list->add_widget(create_remove_audio_device_button(audio_device_list.get()));
return audio_device_list;
}
std::unique_ptr<Button> SettingsPage::create_add_audio_track_button() {
auto add_audio_track_button = std::make_unique<Button>(&get_theme().body_font, "Add audio track", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
auto add_audio_track = [this]() {
audio_devices_list_ptr->add_widget(create_audio_track());
std::unique_ptr<Button> SettingsPage::create_add_audio_device_button() {
auto add_audio_track_button = std::make_unique<Button>(&get_theme().body_font, "Add audio device", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
add_audio_track_button->on_click = [this]() {
audio_devices_list_ptr->add_widget(create_audio_device());
};
add_audio_track_button->on_click = add_audio_track;
return add_audio_track_button;
}
std::unique_ptr<List> SettingsPage::create_audio_track_section() {
std::unique_ptr<List> SettingsPage::create_audio_device_track_section() {
auto audio_devices_list = std::make_unique<List>(List::Orientation::VERTICAL);
audio_devices_list_ptr = audio_devices_list.get();
audio_devices_list_ptr->add_widget(create_audio_track());
audio_devices_list_ptr->add_widget(create_audio_device());
return audio_devices_list;
}
@@ -223,13 +227,107 @@ namespace gsr {
return merge_audio_tracks_checkbox;
}
std::unique_ptr<RadioButton> SettingsPage::create_audio_type_button() {
auto audio_type_radio_button = std::make_unique<RadioButton>(&get_theme().body_font);
audio_type_radio_button->add_item("Audio devices", "audio_devices");
audio_type_radio_button->add_item("Application audio", "app_audio");
audio_type_radio_button_ptr = audio_type_radio_button.get();
return audio_type_radio_button;
}
std::unique_ptr<Widget> SettingsPage::create_audio_device_section() {
auto audio_devices_section_list = std::make_unique<List>(List::Orientation::VERTICAL);
audio_devices_section_list_ptr = audio_devices_section_list.get();
audio_devices_section_list->add_widget(std::make_unique<Label>(&get_theme().title_font, "Audio devices", get_color_theme().text_color));
audio_devices_section_list->add_widget(create_add_audio_device_button());
audio_devices_section_list->add_widget(create_audio_device_track_section());
return audio_devices_section_list;
}
std::unique_ptr<ComboBox> SettingsPage::create_application_audio_selection_combobox() {
auto audio_device_box = std::make_unique<ComboBox>(&get_theme().body_font);
for(const auto &app_audio : application_audio) {
audio_device_box->add_item(app_audio, app_audio);
}
return audio_device_box;
}
std::unique_ptr<Button> SettingsPage::create_remove_application_audio_button(List *app_audio_item) {
auto remove_audio_track_button = std::make_unique<Button>(&get_theme().body_font, "Remove", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
remove_audio_track_button->on_click = [this, app_audio_item]() {
application_audio_list_ptr->remove_widget(app_audio_item);
};
return remove_audio_track_button;
}
std::unique_ptr<List> SettingsPage::create_application_audio() {
auto application_audio_list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
application_audio_list->add_widget(create_application_audio_selection_combobox());
application_audio_list->add_widget(create_remove_application_audio_button(application_audio_list.get()));
return application_audio_list;
}
std::unique_ptr<List> SettingsPage::create_custom_application_audio() {
auto application_audio_list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
application_audio_list->add_widget(std::make_unique<Entry>(&get_theme().body_font, "", (int)(get_theme().body_font.get_character_size() * 10.0f)));
application_audio_list->add_widget(create_remove_application_audio_button(application_audio_list.get()));
return application_audio_list;
}
std::unique_ptr<List> SettingsPage::create_add_application_audio_buttons() {
auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
auto add_application_audio_button = std::make_unique<Button>(&get_theme().body_font, "Add application audio", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
add_application_audio_button->on_click = [this]() {
application_audio_list_ptr->add_widget(create_application_audio());
};
list->add_widget(std::move(add_application_audio_button));
auto add_custom_application_audio_button = std::make_unique<Button>(&get_theme().body_font, "Add custom application audio", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
add_custom_application_audio_button->on_click = [this]() {
application_audio_list_ptr->add_widget(create_custom_application_audio());
};
list->add_widget(std::move(add_custom_application_audio_button));
return list;
}
std::unique_ptr<List> SettingsPage::create_application_audio_track_section() {
auto application_audio_list = std::make_unique<List>(List::Orientation::VERTICAL);
application_audio_list_ptr = application_audio_list.get();
return application_audio_list;
}
std::unique_ptr<CheckBox> SettingsPage::create_application_audio_invert_checkbox() {
auto application_audio_invert_checkbox = std::make_unique<CheckBox>(&get_theme().body_font, "Record all applications except the selected ones");
application_audio_invert_checkbox->set_checked(false);
application_audio_invert_checkbox_ptr = application_audio_invert_checkbox.get();
return application_audio_invert_checkbox;
}
std::unique_ptr<List> SettingsPage::create_application_audio_section() {
auto application_audio_section_list = std::make_unique<List>(List::Orientation::VERTICAL);
application_audio_section_list_ptr = application_audio_section_list.get();
application_audio_section_list->add_widget(std::make_unique<Label>(&get_theme().title_font, "Application audio", get_color_theme().text_color));
application_audio_section_list->add_widget(create_add_application_audio_buttons());
application_audio_section_list->add_widget(create_application_audio_track_section());
application_audio_section_list->add_widget(create_application_audio_invert_checkbox());
return application_audio_section_list;
}
std::unique_ptr<Widget> SettingsPage::create_audio_section() {
auto audio_device_section_list = std::make_unique<List>(List::Orientation::VERTICAL);
audio_device_section_list->add_widget(create_add_audio_track_button());
audio_device_section_list->add_widget(create_audio_track_section());
audio_device_section_list->add_widget(create_merge_audio_tracks_checkbox());
audio_device_section_list->add_widget(create_audio_codec());
return std::make_unique<Subsection>("Audio", std::move(audio_device_section_list), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f));
List *audio_device_section_list_ptr = audio_device_section_list.get();
auto audio_section = std::make_unique<Subsection>("Audio", std::move(audio_device_section_list), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f));
audio_device_section_list_ptr->add_widget(create_audio_type_button());
audio_device_section_list_ptr->add_widget(std::make_unique<LineSeparator>(LineSeparator::Type::HORIZONTAL, audio_section->get_inner_size().x));
audio_device_section_list_ptr->add_widget(create_audio_device_section());
audio_device_section_list_ptr->add_widget(create_application_audio_section());
//audio_device_section_list_ptr->add_widget(std::make_unique<LineSeparator>(LineSeparator::Type::HORIZONTAL, audio_section->get_inner_size().x));
audio_device_section_list_ptr->add_widget(create_merge_audio_tracks_checkbox());
audio_device_section_list_ptr->add_widget(create_audio_codec());
return audio_section;
}
std::unique_ptr<List> SettingsPage::create_video_quality_box() {
@@ -418,7 +516,7 @@ namespace gsr {
auto settings_list = std::make_unique<List>(List::Orientation::VERTICAL);
settings_list->set_spacing(0.018f);
settings_list->add_widget(create_capture_target(gsr_info));
settings_list->add_widget(create_audio_device_section());
settings_list->add_widget(create_audio_section());
settings_list->add_widget(create_video_section(gsr_info));
settings_list_ptr = settings_list.get();
settings_scrollable_page_ptr->add_widget(std::move(settings_list));
@@ -463,6 +561,17 @@ namespace gsr {
record_area_box_ptr->set_selected_item("window");
else
record_area_box_ptr->on_selection_changed("", "");
audio_type_radio_button_ptr->on_selection_changed = [this](const std::string&, const std::string &id) {
if(id == "audio_devices") {
audio_devices_section_list_ptr->set_visible(true);
application_audio_section_list_ptr->set_visible(false);
} else if(id == "app_audio") {
audio_devices_section_list_ptr->set_visible(false);
application_audio_section_list_ptr->set_visible(true);
}
};
audio_type_radio_button_ptr->on_selection_changed("", "audio_devices");
}
void SettingsPage::add_page_specific_widgets() {
@@ -496,7 +605,8 @@ namespace gsr {
select_directory_page->on_click = [this, file_chooser_ptr](const std::string &id) {
if(id == "save")
save_directory_button_ptr->set_text(file_chooser_ptr->get_current_directory());
page_stack->pop();
else if(id == "cancel")
page_stack->pop();
};
page_stack->push(std::move(select_directory_page));
@@ -811,22 +921,50 @@ namespace gsr {
save_config(config);
}
void SettingsPage::load_audio_tracks(RecordOptions &record_options) {
static const std::string* get_application_audio_by_name_case_insensitive(const std::vector<std::string> &application_audio, const std::string &name) {
for(const auto &app_audio : application_audio) {
if(strcasecmp(app_audio.c_str(), name.c_str()) == 0)
return &app_audio;
}
return nullptr;
}
void SettingsPage::load_audio_device_tracks(RecordOptions &record_options) {
audio_devices_list_ptr->clear();
for(const std::string &audio_track : record_options.audio_tracks) {
std::unique_ptr<List> audio_track_widget = create_audio_track();
std::unique_ptr<List> audio_track_widget = create_audio_device();
ComboBox *audio_device_box = static_cast<ComboBox*>(audio_track_widget->get_child_widget_by_index(0));
audio_device_box->set_selected_item(audio_track);
audio_devices_list_ptr->add_widget(std::move(audio_track_widget));
}
}
void SettingsPage::load_application_audio_tracks(RecordOptions &record_options) {
application_audio_list_ptr->clear();
for(const std::string &audio_track : record_options.application_audio) {
const std::string *app_audio = get_application_audio_by_name_case_insensitive(application_audio, audio_track);
if(app_audio) {
std::unique_ptr<List> application_audio_widget = create_application_audio();
ComboBox *application_audio_box = static_cast<ComboBox*>(application_audio_widget->get_child_widget_by_index(0));
application_audio_box->set_selected_item(*app_audio);
application_audio_list_ptr->add_widget(std::move(application_audio_widget));
} else {
std::unique_ptr<List> application_audio_widget = create_custom_application_audio();
Entry *application_audio_entry = static_cast<Entry*>(application_audio_widget->get_child_widget_by_index(0));
application_audio_entry->set_text(audio_track);
application_audio_list_ptr->add_widget(std::move(application_audio_widget));
}
}
}
void SettingsPage::load_common(RecordOptions &record_options) {
record_area_box_ptr->set_selected_item(record_options.record_area_option);
merge_audio_tracks_checkbox_ptr->set_checked(record_options.merge_audio_tracks);
application_audio_invert_checkbox_ptr->set_checked(record_options.application_audio_invert);
change_video_resolution_checkbox_ptr->set_checked(record_options.change_video_resolution);
load_audio_tracks(record_options);
audio_type_radio_button_ptr->set_selected_item(record_options.audio_type_view);
load_audio_device_tracks(record_options);
load_application_audio_tracks(record_options);
color_range_box_ptr->set_selected_item(record_options.color_range);
video_quality_box_ptr->set_selected_item(record_options.video_quality);
video_codec_box_ptr->set_selected_item(record_options.video_codec);
@@ -910,12 +1048,26 @@ namespace gsr {
container_box_ptr->set_selected_item(config.streaming_config.custom.container);
}
static void save_audio_tracks(std::vector<std::string> &audio_tracks, List *audio_devices_list_ptr) {
audio_tracks.clear();
audio_devices_list_ptr->for_each_child_widget([&audio_tracks](std::unique_ptr<Widget> &child_widget) {
static void save_audio_device_tracks(std::vector<std::string> &audio_devices, List *audio_devices_list_ptr) {
audio_devices.clear();
audio_devices_list_ptr->for_each_child_widget([&audio_devices](std::unique_ptr<Widget> &child_widget) {
List *audio_device_line = static_cast<List*>(child_widget.get());
ComboBox *audio_device_box = static_cast<ComboBox*>(audio_device_line->get_child_widget_by_index(0));
audio_tracks.push_back(audio_device_box->get_selected_id());
audio_devices.push_back(audio_device_box->get_selected_id());
return true;
});
}
static void save_application_audio_tracks(std::vector<std::string> &application_audio, List *application_audio_list_ptr) {
application_audio.clear();
application_audio_list_ptr->for_each_child_widget([&application_audio](std::unique_ptr<Widget> &child_widget) {
List *application_audio_line = static_cast<List*>(child_widget.get());
ComboBox *application_audio_box = dynamic_cast<ComboBox*>(application_audio_line->get_child_widget_by_index(0));
Entry *application_audio_entry = dynamic_cast<Entry*>(application_audio_line->get_child_widget_by_index(0));
if(application_audio_box)
application_audio.push_back(application_audio_box->get_selected_id());
else if(application_audio_entry)
application_audio.push_back(application_audio_entry->get_text());
return true;
});
}
@@ -929,8 +1081,11 @@ namespace gsr {
record_options.fps = atoi(framerate_entry_ptr->get_text().c_str());
record_options.video_bitrate = atoi(video_bitrate_entry_ptr->get_text().c_str());
record_options.merge_audio_tracks = merge_audio_tracks_checkbox_ptr->is_checked();
record_options.application_audio_invert = application_audio_invert_checkbox_ptr->is_checked();
record_options.change_video_resolution = change_video_resolution_checkbox_ptr->is_checked();
save_audio_tracks(record_options.audio_tracks, audio_devices_list_ptr);
record_options.audio_type_view = audio_type_radio_button_ptr->get_selected_id();
save_audio_device_tracks(record_options.audio_tracks, audio_devices_list_ptr);
save_application_audio_tracks(record_options.application_audio, application_audio_list_ptr);
record_options.color_range = color_range_box_ptr->get_selected_id();
record_options.video_quality = video_quality_box_ptr->get_selected_id();
record_options.video_codec = video_codec_box_ptr->get_selected_id();

View File

@@ -36,8 +36,11 @@ namespace gsr {
window.draw(background);
draw_pos += mgl::vec2f(margin_left_scale, margin_top_scale) * mgl::vec2f(get_theme().window_height, get_theme().window_height);
label.draw(window, draw_pos);
draw_pos.y += label.get_size().y + title_spacing_scale * get_theme().window_height;
if(!label.get_text().empty()) {
label.draw(window, draw_pos);
draw_pos.y += label.get_size().y + title_spacing_scale * get_theme().window_height;
}
inner_widget->set_position(draw_pos);
inner_widget->draw(window, mgl::vec2f(0.0f, 0.0f));
@@ -48,7 +51,8 @@ namespace gsr {
return {0.0f, 0.0f};
const mgl::vec2f margin_size = mgl::vec2f(margin_left_scale + margin_right_scale, margin_top_scale + margin_bottom_scale) * mgl::vec2f(get_theme().window_height, get_theme().window_height);
mgl::vec2f widgets_size = mgl::vec2f(0.0f, label.get_size().y + title_spacing_scale * get_theme().window_height) + inner_widget->get_size() + margin_size;
const float title_height = !label.get_text().empty() ? (label.get_size().y + title_spacing_scale * get_theme().window_height) : 0.0f;
mgl::vec2f widgets_size = mgl::vec2f(0.0f, title_height) + inner_widget->get_size() + margin_size;
if(std::abs(size.x) > 0.001f)
widgets_size.x = size.x;
@@ -57,4 +61,12 @@ namespace gsr {
return widgets_size;
}
mgl::vec2f Subsection::get_inner_size() {
if(!visible)
return {0.0f, 0.0f};
const mgl::vec2f margin_size = mgl::vec2f(margin_left_scale + margin_right_scale, margin_top_scale + margin_bottom_scale) * mgl::vec2f(get_theme().window_height, get_theme().window_height);
return get_size() - margin_size;
}
}