mirror of
https://repo.dec05eba.com/gpu-screen-recorder
synced 2026-04-06 03:26:38 +09:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
815350a5ca | ||
|
|
23fd8065b6 |
4
TODO
4
TODO
@@ -177,9 +177,7 @@ Test if `xrandr --output DP-1 --scale 1.5` captures correct size on nvidia.
|
||||
|
||||
Fix cursor position and scale when scaling x11 display.
|
||||
|
||||
Support surround audio in application audio recording. Right now only stereo and mono sound is supported.
|
||||
|
||||
Support application audio recording without pulseaudio combined sink.
|
||||
Support application audio recording without pipewire combined sink.
|
||||
|
||||
Support transposing (rotating) with vaapi. This isn't supported on many devices with rgb buffer, but its supported with nv12 buffer (on intel at least).
|
||||
|
||||
|
||||
@@ -8,12 +8,6 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#define GSR_PIPEWIRE_AUDIO_MAX_STREAM_NODES 128
|
||||
#define GSR_PIPEWIRE_AUDIO_MAX_PORTS 256
|
||||
#define GSR_PIPEWIRE_AUDIO_MAX_LINKS 256
|
||||
#define GSR_PIPEWIRE_AUDIO_MAX_REQUESTED_LINKS 32
|
||||
#define GSR_PIPEWIRE_AUDIO_MAX_VIRTUAL_SINKS 32
|
||||
|
||||
typedef enum {
|
||||
GSR_PIPEWIRE_AUDIO_NODE_TYPE_STREAM_OUTPUT, /* Application audio */
|
||||
GSR_PIPEWIRE_AUDIO_NODE_TYPE_STREAM_INPUT, /* Audio recording input */
|
||||
@@ -84,20 +78,25 @@ typedef struct {
|
||||
char default_output_device_name[128];
|
||||
char default_input_device_name[128];
|
||||
|
||||
gsr_pipewire_audio_node stream_nodes[GSR_PIPEWIRE_AUDIO_MAX_STREAM_NODES];
|
||||
int num_stream_nodes;
|
||||
gsr_pipewire_audio_node *stream_nodes;
|
||||
size_t num_stream_nodes;
|
||||
size_t stream_nodes_capacity_items;
|
||||
|
||||
gsr_pipewire_audio_port ports[GSR_PIPEWIRE_AUDIO_MAX_PORTS];
|
||||
int num_ports;
|
||||
gsr_pipewire_audio_port *ports;
|
||||
size_t num_ports;
|
||||
size_t ports_capacity_items;
|
||||
|
||||
gsr_pipewire_audio_link links[GSR_PIPEWIRE_AUDIO_MAX_LINKS];
|
||||
int num_links;
|
||||
gsr_pipewire_audio_link *links;
|
||||
size_t num_links;
|
||||
size_t links_capacity_items;
|
||||
|
||||
gsr_pipewire_audio_requested_link requested_links[GSR_PIPEWIRE_AUDIO_MAX_REQUESTED_LINKS];
|
||||
int num_requested_links;
|
||||
gsr_pipewire_audio_requested_link *requested_links;
|
||||
size_t num_requested_links;
|
||||
size_t requested_links_capacity_items;
|
||||
|
||||
struct pw_proxy *virtual_sink_proxies[GSR_PIPEWIRE_AUDIO_MAX_VIRTUAL_SINKS];
|
||||
int num_virtual_sink_proxies;
|
||||
struct pw_proxy **virtual_sink_proxies;
|
||||
size_t num_virtual_sink_proxies;
|
||||
size_t virtual_sink_proxies_capacity_items;
|
||||
} gsr_pipewire_audio;
|
||||
|
||||
bool gsr_pipewire_audio_init(gsr_pipewire_audio *self);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
project('gpu-screen-recorder', ['c', 'cpp'], version : '5.5.4', default_options : ['warning_level=2'])
|
||||
project('gpu-screen-recorder', ['c', 'cpp'], version : '5.5.5', default_options : ['warning_level=2'])
|
||||
|
||||
add_project_arguments('-Wshadow', language : ['c', 'cpp'])
|
||||
if get_option('buildtype') == 'debug'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "gpu-screen-recorder"
|
||||
type = "executable"
|
||||
version = "5.5.4"
|
||||
version = "5.5.5"
|
||||
platforms = ["posix"]
|
||||
|
||||
[config]
|
||||
|
||||
@@ -465,7 +465,7 @@ static bool args_parser_set_values(args_parser *self) {
|
||||
self->keyint = args_get_double_by_key(self->args, NUM_ARGS, "-keyint", 2.0);
|
||||
|
||||
if(self->audio_codec == GSR_AUDIO_CODEC_FLAC) {
|
||||
fprintf(stderr, "Warning: flac audio codec is temporary disabled, using opus audio codec instead\n");
|
||||
fprintf(stderr, "gsr warning: flac audio codec is temporary disabled, using opus audio codec instead\n");
|
||||
self->audio_codec = GSR_AUDIO_CODEC_OPUS;
|
||||
}
|
||||
|
||||
@@ -473,7 +473,7 @@ static bool args_parser_set_values(args_parser *self) {
|
||||
if(self->portal_session_token_filepath) {
|
||||
int len = strlen(self->portal_session_token_filepath);
|
||||
if(len > 0 && self->portal_session_token_filepath[len - 1] == '/') {
|
||||
fprintf(stderr, "Error: -portal-session-token-filepath should be a path to a file but it ends with a /: %s\n", self->portal_session_token_filepath);
|
||||
fprintf(stderr, "gsr error: -portal-session-token-filepath should be a path to a file but it ends with a /: %s\n", self->portal_session_token_filepath);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -482,13 +482,13 @@ static bool args_parser_set_values(args_parser *self) {
|
||||
if(self->recording_saved_script) {
|
||||
struct stat buf;
|
||||
if(stat(self->recording_saved_script, &buf) == -1 || !S_ISREG(buf.st_mode)) {
|
||||
fprintf(stderr, "Error: Script \"%s\" either doesn't exist or it's not a file\n", self->recording_saved_script);
|
||||
fprintf(stderr, "gsr error: Script \"%s\" either doesn't exist or it's not a file\n", self->recording_saved_script);
|
||||
usage();
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!(buf.st_mode & S_IXUSR)) {
|
||||
fprintf(stderr, "Error: Script \"%s\" is not executable\n", self->recording_saved_script);
|
||||
fprintf(stderr, "gsr error: Script \"%s\" is not executable\n", self->recording_saved_script);
|
||||
usage();
|
||||
return false;
|
||||
}
|
||||
@@ -500,19 +500,19 @@ static bool args_parser_set_values(args_parser *self) {
|
||||
|
||||
if(self->bitrate_mode == GSR_BITRATE_MODE_CBR) {
|
||||
if(!quality_str) {
|
||||
fprintf(stderr, "Error: option '-q' is required when using '-bm cbr' option\n");
|
||||
fprintf(stderr, "gsr error: option '-q' is required when using '-bm cbr' option\n");
|
||||
usage();
|
||||
return false;
|
||||
}
|
||||
|
||||
if(sscanf(quality_str, "%" PRIi64, &self->video_bitrate) != 1) {
|
||||
fprintf(stderr, "Error: -q argument \"%s\" is not an integer value. When using '-bm cbr' option '-q' is expected to be an integer value\n", quality_str);
|
||||
fprintf(stderr, "gsr error: -q argument \"%s\" is not an integer value. When using '-bm cbr' option '-q' is expected to be an integer value\n", quality_str);
|
||||
usage();
|
||||
return false;
|
||||
}
|
||||
|
||||
if(self->video_bitrate < 0) {
|
||||
fprintf(stderr, "Error: -q is expected to be 0 or larger, got %" PRIi64 "\n", self->video_bitrate);
|
||||
fprintf(stderr, "gsr error: -q is expected to be 0 or larger, got %" PRIi64 "\n", self->video_bitrate);
|
||||
usage();
|
||||
return false;
|
||||
}
|
||||
@@ -531,7 +531,7 @@ static bool args_parser_set_values(args_parser *self) {
|
||||
} else if(strcmp(quality_str, "ultra") == 0) {
|
||||
self->video_quality = GSR_VIDEO_QUALITY_ULTRA;
|
||||
} else {
|
||||
fprintf(stderr, "Error: -q should either be 'medium', 'high', 'very_high' or 'ultra', got: '%s'\n", quality_str);
|
||||
fprintf(stderr, "gsr error: -q should either be 'medium', 'high', 'very_high' or 'ultra', got: '%s'\n", quality_str);
|
||||
usage();
|
||||
return false;
|
||||
}
|
||||
@@ -539,7 +539,7 @@ static bool args_parser_set_values(args_parser *self) {
|
||||
|
||||
const char *output_resolution_str = args_get_value_by_key(self->args, NUM_ARGS, "-s");
|
||||
if(!output_resolution_str && strcmp(self->window, "focused") == 0) {
|
||||
fprintf(stderr, "Error: option -s is required when using '-w focused' option\n");
|
||||
fprintf(stderr, "gsr error: option -s is required when using '-w focused' option\n");
|
||||
usage();
|
||||
return false;
|
||||
}
|
||||
@@ -547,13 +547,13 @@ static bool args_parser_set_values(args_parser *self) {
|
||||
self->output_resolution = (vec2i){0, 0};
|
||||
if(output_resolution_str) {
|
||||
if(sscanf(output_resolution_str, "%dx%d", &self->output_resolution.x, &self->output_resolution.y) != 2) {
|
||||
fprintf(stderr, "Error: invalid value for option -s '%s', expected a value in format WxH\n", output_resolution_str);
|
||||
fprintf(stderr, "gsr error: invalid value for option -s '%s', expected a value in format WxH\n", output_resolution_str);
|
||||
usage();
|
||||
return false;
|
||||
}
|
||||
|
||||
if(self->output_resolution.x < 0 || self->output_resolution.y < 0) {
|
||||
fprintf(stderr, "Error: invalid value for option -s '%s', expected width and height to be greater or equal to 0\n", output_resolution_str);
|
||||
fprintf(stderr, "gsr error: invalid value for option -s '%s', expected width and height to be greater or equal to 0\n", output_resolution_str);
|
||||
usage();
|
||||
return false;
|
||||
}
|
||||
@@ -564,25 +564,25 @@ static bool args_parser_set_values(args_parser *self) {
|
||||
const char *region_str = args_get_value_by_key(self->args, NUM_ARGS, "-region");
|
||||
if(region_str) {
|
||||
if(strcmp(self->window, "region") != 0) {
|
||||
fprintf(stderr, "Error: option -region can only be used when option '-w region' is used\n");
|
||||
fprintf(stderr, "gsr error: option -region can only be used when option '-w region' is used\n");
|
||||
usage();
|
||||
return false;
|
||||
}
|
||||
|
||||
if(sscanf(region_str, "%dx%d+%d+%d", &self->region_size.x, &self->region_size.y, &self->region_position.x, &self->region_position.y) != 4) {
|
||||
fprintf(stderr, "Error: invalid value for option -region '%s', expected a value in format WxH+X+Y\n", region_str);
|
||||
fprintf(stderr, "gsr error: invalid value for option -region '%s', expected a value in format WxH+X+Y\n", region_str);
|
||||
usage();
|
||||
return false;
|
||||
}
|
||||
|
||||
if(self->region_size.x < 0 || self->region_size.y < 0 || self->region_position.x < 0 || self->region_position.y < 0) {
|
||||
fprintf(stderr, "Error: invalid value for option -region '%s', expected width, height, x and y to be greater or equal to 0\n", region_str);
|
||||
fprintf(stderr, "gsr error: invalid value for option -region '%s', expected width, height, x and y to be greater or equal to 0\n", region_str);
|
||||
usage();
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if(strcmp(self->window, "region") == 0) {
|
||||
fprintf(stderr, "Error: option -region is required when '-w region' is used\n");
|
||||
fprintf(stderr, "gsr error: option -region is required when '-w region' is used\n");
|
||||
usage();
|
||||
return false;
|
||||
}
|
||||
@@ -604,7 +604,7 @@ static bool args_parser_set_values(args_parser *self) {
|
||||
self->is_livestream = is_livestream_path(self->filename);
|
||||
if(self->is_livestream) {
|
||||
if(is_replaying) {
|
||||
fprintf(stderr, "Error: replay mode is not applicable to live streaming\n");
|
||||
fprintf(stderr, "gsr error: replay mode is not applicable to live streaming\n");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
@@ -614,20 +614,20 @@ static bool args_parser_set_values(args_parser *self) {
|
||||
char *directory = dirname(directory_buf);
|
||||
if(strcmp(directory, ".") != 0 && strcmp(directory, "/") != 0) {
|
||||
if(create_directory_recursive(directory) != 0) {
|
||||
fprintf(stderr, "Error: failed to create directory for output file: %s\n", self->filename);
|
||||
fprintf(stderr, "gsr error: failed to create directory for output file: %s\n", self->filename);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(!self->container_format) {
|
||||
fprintf(stderr, "Error: option -c is required when using option -r\n");
|
||||
fprintf(stderr, "gsr error: option -c is required when using option -r\n");
|
||||
usage();
|
||||
return false;
|
||||
}
|
||||
|
||||
struct stat buf;
|
||||
if(stat(self->filename, &buf) != -1 && !S_ISDIR(buf.st_mode)) {
|
||||
fprintf(stderr, "Error: File \"%s\" exists but it's not a directory\n", self->filename);
|
||||
fprintf(stderr, "gsr error: File \"%s\" exists but it's not a directory\n", self->filename);
|
||||
usage();
|
||||
return false;
|
||||
}
|
||||
@@ -637,13 +637,13 @@ static bool args_parser_set_values(args_parser *self) {
|
||||
if(!is_replaying) {
|
||||
self->filename = "/dev/stdout";
|
||||
} else {
|
||||
fprintf(stderr, "Error: Option -o is required when using option -r\n");
|
||||
fprintf(stderr, "gsr error: Option -o is required when using option -r\n");
|
||||
usage();
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!self->container_format) {
|
||||
fprintf(stderr, "Error: option -c is required when not using option -o\n");
|
||||
fprintf(stderr, "gsr error: option -c is required when not using option -o\n");
|
||||
usage();
|
||||
return false;
|
||||
}
|
||||
@@ -656,10 +656,10 @@ static bool args_parser_set_values(args_parser *self) {
|
||||
|
||||
const bool is_portal_capture = strcmp(self->window, "portal") == 0;
|
||||
if(!self->restore_portal_session && is_portal_capture)
|
||||
fprintf(stderr, "Info: option '-w portal' was used without '-restore-portal-session yes'. The previous screencast session will be ignored\n");
|
||||
fprintf(stderr, "gsr info: option '-w portal' was used without '-restore-portal-session yes'. The previous screencast session will be ignored\n");
|
||||
|
||||
if(self->is_livestream && self->recording_saved_script) {
|
||||
fprintf(stderr, "Warning: live stream detected, -sc script is ignored\n");
|
||||
fprintf(stderr, "gsr warning: live stream detected, -sc script is ignored\n");
|
||||
self->recording_saved_script = NULL;
|
||||
}
|
||||
|
||||
@@ -704,7 +704,7 @@ bool args_parser_parse(args_parser *self, int argc, char **argv, const args_hand
|
||||
arg_handlers->list_capture_options(card_path, userdata);
|
||||
return true;
|
||||
} else {
|
||||
fprintf(stderr, "Error: expected --list-capture-options to be called with either no extra arguments or 1 extra argument (card path)\n");
|
||||
fprintf(stderr, "gsr error: expected --list-capture-options to be called with either no extra arguments or 1 extra argument (card path)\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -751,19 +751,19 @@ bool args_parser_parse(args_parser *self, int argc, char **argv, const args_hand
|
||||
const char *arg_name = argv[i];
|
||||
Arg *arg = args_get_by_key(self->args, NUM_ARGS, arg_name);
|
||||
if(!arg) {
|
||||
fprintf(stderr, "Error: invalid argument '%s'\n", arg_name);
|
||||
fprintf(stderr, "gsr error: invalid argument '%s'\n", arg_name);
|
||||
usage();
|
||||
return false;
|
||||
}
|
||||
|
||||
if(arg->num_values > 0 && !arg->list) {
|
||||
fprintf(stderr, "Error: expected argument '%s' to only be specified once\n", arg_name);
|
||||
fprintf(stderr, "gsr error: expected argument '%s' to only be specified once\n", arg_name);
|
||||
usage();
|
||||
return false;
|
||||
}
|
||||
|
||||
if(i + 1 >= argc) {
|
||||
fprintf(stderr, "Error: missing value for argument '%s'\n", arg_name);
|
||||
fprintf(stderr, "gsr error: missing value for argument '%s'\n", arg_name);
|
||||
usage();
|
||||
return false;
|
||||
}
|
||||
@@ -779,7 +779,7 @@ bool args_parser_parse(args_parser *self, int argc, char **argv, const args_hand
|
||||
} else if(strcmp(arg_value, "no") == 0) {
|
||||
arg->typed_value.boolean = false;
|
||||
} else {
|
||||
fprintf(stderr, "Error: %s should either be 'yes' or 'no', got: '%s'\n", arg_name, arg_value);
|
||||
fprintf(stderr, "gsr error: %s should either be 'yes' or 'no', got: '%s'\n", arg_name, arg_value);
|
||||
usage();
|
||||
return false;
|
||||
}
|
||||
@@ -787,7 +787,7 @@ bool args_parser_parse(args_parser *self, int argc, char **argv, const args_hand
|
||||
}
|
||||
case ARG_TYPE_ENUM: {
|
||||
if(!arg_get_enum_value_by_name(arg, arg_value, &arg->typed_value.enum_value)) {
|
||||
fprintf(stderr, "Error: %s should either be ", arg_name);
|
||||
fprintf(stderr, "gsr error: %s should either be ", arg_name);
|
||||
arg_print_expected_enum_names(arg);
|
||||
fprintf(stderr, ", got: '%s'\n", arg_value);
|
||||
usage();
|
||||
@@ -797,19 +797,19 @@ bool args_parser_parse(args_parser *self, int argc, char **argv, const args_hand
|
||||
}
|
||||
case ARG_TYPE_I64: {
|
||||
if(sscanf(arg_value, "%" PRIi64, &arg->typed_value.i64_value) != 1) {
|
||||
fprintf(stderr, "Error: %s argument \"%s\" is not an integer\n", arg_name, arg_value);
|
||||
fprintf(stderr, "gsr error: %s argument \"%s\" is not an integer\n", arg_name, arg_value);
|
||||
usage();
|
||||
return false;
|
||||
}
|
||||
|
||||
if(arg->typed_value.i64_value < arg->integer_value_min) {
|
||||
fprintf(stderr, "Error: %s argument is expected to be larger than %" PRIi64 ", got %" PRIi64 "\n", arg_name, arg->integer_value_min, arg->typed_value.i64_value);
|
||||
fprintf(stderr, "gsr error: %s argument is expected to be larger than %" PRIi64 ", got %" PRIi64 "\n", arg_name, arg->integer_value_min, arg->typed_value.i64_value);
|
||||
usage();
|
||||
return false;
|
||||
}
|
||||
|
||||
if(arg->typed_value.i64_value > arg->integer_value_max) {
|
||||
fprintf(stderr, "Error: %s argument is expected to be less than %" PRIi64 ", got %" PRIi64 "\n", arg_name, arg->integer_value_max, arg->typed_value.i64_value);
|
||||
fprintf(stderr, "gsr error: %s argument is expected to be less than %" PRIi64 ", got %" PRIi64 "\n", arg_name, arg->integer_value_max, arg->typed_value.i64_value);
|
||||
usage();
|
||||
return false;
|
||||
}
|
||||
@@ -817,19 +817,19 @@ bool args_parser_parse(args_parser *self, int argc, char **argv, const args_hand
|
||||
}
|
||||
case ARG_TYPE_DOUBLE: {
|
||||
if(sscanf(arg_value, "%lf", &arg->typed_value.d_value) != 1) {
|
||||
fprintf(stderr, "Error: %s argument \"%s\" is not an floating-point number\n", arg_name, arg_value);
|
||||
fprintf(stderr, "gsr error: %s argument \"%s\" is not an floating-point number\n", arg_name, arg_value);
|
||||
usage();
|
||||
return false;
|
||||
}
|
||||
|
||||
if(arg->typed_value.d_value < arg->integer_value_min) {
|
||||
fprintf(stderr, "Error: %s argument is expected to be larger than %" PRIi64 ", got %lf\n", arg_name, arg->integer_value_min, arg->typed_value.d_value);
|
||||
fprintf(stderr, "gsr error: %s argument is expected to be larger than %" PRIi64 ", got %lf\n", arg_name, arg->integer_value_min, arg->typed_value.d_value);
|
||||
usage();
|
||||
return false;
|
||||
}
|
||||
|
||||
if(arg->typed_value.d_value > arg->integer_value_max) {
|
||||
fprintf(stderr, "Error: %s argument is expected to be less than %" PRIi64 ", got %lf\n", arg_name, arg->integer_value_max, arg->typed_value.d_value);
|
||||
fprintf(stderr, "gsr error: %s argument is expected to be less than %" PRIi64 ", got %lf\n", arg_name, arg->integer_value_max, arg->typed_value.d_value);
|
||||
usage();
|
||||
return false;
|
||||
}
|
||||
@@ -838,7 +838,7 @@ bool args_parser_parse(args_parser *self, int argc, char **argv, const args_hand
|
||||
}
|
||||
|
||||
if(!arg_append_value(arg, arg_value)) {
|
||||
fprintf(stderr, "Error: failed to append argument, out of memory\n");
|
||||
fprintf(stderr, "gsr error: failed to append argument, out of memory\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -846,7 +846,7 @@ bool args_parser_parse(args_parser *self, int argc, char **argv, const args_hand
|
||||
for(int i = 0; i < NUM_ARGS; ++i) {
|
||||
const Arg *arg = &self->args[i];
|
||||
if(!arg->optional && arg->num_values == 0) {
|
||||
fprintf(stderr, "Error: missing argument '%s'\n", arg->key);
|
||||
fprintf(stderr, "gsr error: missing argument '%s'\n", arg->key);
|
||||
usage();
|
||||
return false;
|
||||
}
|
||||
@@ -870,45 +870,45 @@ bool args_parser_validate_with_gl_info(args_parser *self, gsr_egl *egl) {
|
||||
}
|
||||
|
||||
if(egl->gpu_info.is_steam_deck && self->bitrate_mode == GSR_BITRATE_MODE_QP) {
|
||||
fprintf(stderr, "Warning: qp bitrate mode is not supported on Steam Deck because of Steam Deck driver bugs. Using vbr instead\n");
|
||||
fprintf(stderr, "gsr warning: qp bitrate mode is not supported on Steam Deck because of Steam Deck driver bugs. Using vbr instead\n");
|
||||
self->bitrate_mode = GSR_BITRATE_MODE_VBR;
|
||||
}
|
||||
|
||||
if(self->video_encoder == GSR_VIDEO_ENCODER_HW_CPU && self->bitrate_mode == GSR_BITRATE_MODE_VBR) {
|
||||
fprintf(stderr, "Warning: bitrate mode has been forcefully set to qp because software encoding option doesn't support vbr option\n");
|
||||
fprintf(stderr, "gsr warning: bitrate mode has been forcefully set to qp because software encoding option doesn't support vbr option\n");
|
||||
self->bitrate_mode = GSR_BITRATE_MODE_QP;
|
||||
}
|
||||
|
||||
if(egl->gpu_info.vendor != GSR_GPU_VENDOR_NVIDIA && self->overclock) {
|
||||
fprintf(stderr, "Info: overclock option has no effect on amd/intel, ignoring option\n");
|
||||
fprintf(stderr, "gsr info: overclock option has no effect on amd/intel, ignoring option\n");
|
||||
self->overclock = false;
|
||||
}
|
||||
|
||||
if(egl->gpu_info.vendor == GSR_GPU_VENDOR_NVIDIA && self->overclock && wayland) {
|
||||
fprintf(stderr, "Info: overclocking is not possible on nvidia on wayland, ignoring option\n");
|
||||
fprintf(stderr, "gsr info: overclocking is not possible on nvidia on wayland, ignoring option\n");
|
||||
self->overclock = false;
|
||||
}
|
||||
|
||||
if(egl->gpu_info.is_steam_deck) {
|
||||
fprintf(stderr, "Warning: steam deck has multiple driver issues. One of them has been reported here: https://github.com/ValveSoftware/SteamOS/issues/1609\n"
|
||||
fprintf(stderr, "gsr warning: steam deck has multiple driver issues. One of them has been reported here: https://github.com/ValveSoftware/SteamOS/issues/1609\n"
|
||||
"If you have issues with GPU Screen Recorder on steam deck that you don't have on a desktop computer then report the issue to Valve and/or AMD.\n");
|
||||
}
|
||||
|
||||
self->very_old_gpu = false;
|
||||
if(egl->gpu_info.vendor == GSR_GPU_VENDOR_NVIDIA && egl->gpu_info.gpu_version != 0 && egl->gpu_info.gpu_version < 900) {
|
||||
fprintf(stderr, "Info: your gpu appears to be very old (older than maxwell architecture). Switching to lower preset\n");
|
||||
fprintf(stderr, "gsr info: your gpu appears to be very old (older than maxwell architecture). Switching to lower preset\n");
|
||||
self->very_old_gpu = true;
|
||||
}
|
||||
|
||||
if(video_codec_is_hdr(self->video_codec) && !wayland) {
|
||||
fprintf(stderr, "Error: hdr video codec option %s is not available on X11\n", video_codec_to_string(self->video_codec));
|
||||
fprintf(stderr, "gsr error: hdr video codec option %s is not available on X11\n", video_codec_to_string(self->video_codec));
|
||||
usage();
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool is_portal_capture = strcmp(self->window, "portal") == 0;
|
||||
if(video_codec_is_hdr(self->video_codec) && is_portal_capture) {
|
||||
fprintf(stderr, "Warning: portal capture option doesn't support hdr yet (PipeWire doesn't support hdr), the video will be tonemapped from hdr to sdr\n");
|
||||
fprintf(stderr, "gsr warning: portal capture option doesn't support hdr yet (PipeWire doesn't support hdr), the video will be tonemapped from hdr to sdr\n");
|
||||
self->video_codec = hdr_video_codec_to_sdr_video_codec(self->video_codec);
|
||||
}
|
||||
|
||||
|
||||
@@ -289,12 +289,12 @@ static int gsr_capture_nvfbc_start(gsr_capture *cap, gsr_capture_metadata *captu
|
||||
int driver_major_version = 0;
|
||||
int driver_minor_version = 0;
|
||||
if(self->params.direct_capture && get_driver_version(&driver_major_version, &driver_minor_version)) {
|
||||
fprintf(stderr, "Info: detected nvidia version: %d.%d\n", driver_major_version, driver_minor_version);
|
||||
fprintf(stderr, "gsr info: detected nvidia version: %d.%d\n", driver_major_version, driver_minor_version);
|
||||
|
||||
// TODO:
|
||||
if(version_at_least(driver_major_version, driver_minor_version, 515, 57) && version_less_than(driver_major_version, driver_minor_version, 520, 56)) {
|
||||
self->params.direct_capture = false;
|
||||
fprintf(stderr, "Warning: \"screen-direct\" has temporary been disabled as it causes stuttering with driver versions >= 515.57 and < 520.56. Please update your driver if possible. Capturing \"screen\" instead.\n");
|
||||
fprintf(stderr, "gsr warning: \"screen-direct\" has temporary been disabled as it causes stuttering with driver versions >= 515.57 and < 520.56. Please update your driver if possible. Capturing \"screen\" instead.\n");
|
||||
}
|
||||
|
||||
// TODO:
|
||||
@@ -304,7 +304,7 @@ static int gsr_capture_nvfbc_start(gsr_capture *cap, gsr_capture_metadata *captu
|
||||
if(version_at_least(driver_major_version, driver_minor_version, 515, 57))
|
||||
self->supports_direct_cursor = true;
|
||||
else
|
||||
fprintf(stderr, "Info: capturing \"screen-direct\" but driver version appears to be less than 515.57. Disabling capture of cursor. Please update your driver if you want to capture your cursor or record \"screen\" instead.\n");
|
||||
fprintf(stderr, "gsr info: capturing \"screen-direct\" but driver version appears to be less than 515.57. Disabling capture of cursor. Please update your driver if you want to capture your cursor or record \"screen\" instead.\n");
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
157
src/main.cpp
157
src/main.cpp
@@ -196,7 +196,7 @@ static AVSampleFormat audio_codec_get_sample_format(AVCodecContext *audio_codec_
|
||||
supports_s16 = false;
|
||||
|
||||
if(!supports_s16 && !supports_flt) {
|
||||
fprintf(stderr, "Warning: opus audio codec is chosen but your ffmpeg version does not support s16/flt sample format and performance might be slightly worse.\n");
|
||||
fprintf(stderr, "gsr warning: opus audio codec is chosen but your ffmpeg version does not support s16/flt sample format and performance might be slightly worse.\n");
|
||||
fprintf(stderr, " You can either rebuild ffmpeg with libopus instead of the built-in opus, use the flatpak version of gpu screen recorder or record with aac audio codec instead (-ac aac).\n");
|
||||
fprintf(stderr, " Falling back to fltp audio sample format instead.\n");
|
||||
}
|
||||
@@ -250,7 +250,7 @@ static AVCodecContext* create_audio_codec_context(int fps, gsr_audio_codec audio
|
||||
(void)fps;
|
||||
const AVCodec *codec = avcodec_find_encoder(audio_codec_get_id(audio_codec));
|
||||
if (!codec) {
|
||||
fprintf(stderr, "Error: Could not find %s audio encoder\n", audio_codec_get_name(audio_codec));
|
||||
fprintf(stderr, "gsr error: Could not find %s audio encoder\n", audio_codec_get_name(audio_codec));
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
@@ -649,7 +649,6 @@ static void video_software_set_qp(AVCodecContext *codec_context, gsr_video_quali
|
||||
}
|
||||
|
||||
static void open_video_software(AVCodecContext *codec_context, const args_parser &arg_parser) {
|
||||
const gsr_color_depth color_depth = video_codec_to_bit_depth(arg_parser.video_codec);
|
||||
const bool hdr = video_codec_is_hdr(arg_parser.video_codec);
|
||||
AVDictionary *options = nullptr;
|
||||
|
||||
@@ -668,7 +667,7 @@ static void open_video_software(AVCodecContext *codec_context, const args_parser
|
||||
|
||||
int ret = avcodec_open2(codec_context, codec_context->codec, &options);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Error: Could not open video codec: %s\n", av_error_to_string(ret));
|
||||
fprintf(stderr, "gsr error: Could not open video codec: %s\n", av_error_to_string(ret));
|
||||
_exit(1);
|
||||
}
|
||||
}
|
||||
@@ -916,7 +915,7 @@ static void open_video_hardware(AVCodecContext *codec_context, bool low_power, c
|
||||
|
||||
int ret = avcodec_open2(codec_context, codec_context->codec, &options);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Error: Could not open video codec: %s\n", av_error_to_string(ret));
|
||||
fprintf(stderr, "gsr error: Could not open video codec: %s\n", av_error_to_string(ret));
|
||||
_exit(1);
|
||||
}
|
||||
}
|
||||
@@ -1022,7 +1021,7 @@ static std::string get_time_only_str() {
|
||||
static AVStream* create_stream(AVFormatContext *av_format_context, AVCodecContext *codec_context) {
|
||||
AVStream *stream = avformat_new_stream(av_format_context, nullptr);
|
||||
if (!stream) {
|
||||
fprintf(stderr, "Error: Could not allocate stream\n");
|
||||
fprintf(stderr, "gsr error: Could not allocate stream\n");
|
||||
_exit(1);
|
||||
}
|
||||
stream->id = av_format_context->nb_streams - 1;
|
||||
@@ -1035,7 +1034,7 @@ static void run_recording_saved_script_async(const char *script_file, const char
|
||||
char script_file_full[PATH_MAX];
|
||||
script_file_full[0] = '\0';
|
||||
if(!realpath(script_file, script_file_full)) {
|
||||
fprintf(stderr, "Error: script file not found: %s\n", script_file);
|
||||
fprintf(stderr, "gsr error: script file not found: %s\n", script_file);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1240,11 +1239,11 @@ static std::string create_new_recording_filepath_from_timestamp(std::string dire
|
||||
if(date_folders) {
|
||||
std::string output_folder = directory + '/' + get_date_only_str();
|
||||
if(create_directory_recursive(&output_folder[0]) != 0)
|
||||
fprintf(stderr, "Error: failed to create directory: %s\n", output_folder.c_str());
|
||||
fprintf(stderr, "gsr error: failed to create directory: %s\n", output_folder.c_str());
|
||||
output_filepath = output_folder + "/" + filename_prefix + "_" + get_time_only_str() + "." + file_extension;
|
||||
} else {
|
||||
if(create_directory_recursive(&directory[0]) != 0)
|
||||
fprintf(stderr, "Error: failed to create directory: %s\n", directory.c_str());
|
||||
fprintf(stderr, "gsr error: failed to create directory: %s\n", directory.c_str());
|
||||
output_filepath = directory + "/" + filename_prefix + "_" + get_date_str() + "." + file_extension;
|
||||
}
|
||||
return output_filepath;
|
||||
@@ -1349,7 +1348,7 @@ static void save_replay_async(AVCodecContext *video_codec_context, int video_str
|
||||
|
||||
const int ret = av_write_frame(recording_start_result.av_format_context, &av_packet);
|
||||
if(ret < 0)
|
||||
fprintf(stderr, "Error: Failed to write frame index %d to muxer, reason: %s (%d)\n", av_packet.stream_index, av_error_to_string(ret), ret);
|
||||
fprintf(stderr, "gsr error: Failed to write frame index %d to muxer, reason: %s (%d)\n", av_packet.stream_index, av_error_to_string(ret), ret);
|
||||
|
||||
free(replay_packet_data);
|
||||
|
||||
@@ -1501,7 +1500,7 @@ static int init_filter_graph(AVCodecContext* audio_codec_context, AVFilterGraph*
|
||||
snprintf(args, sizeof(args), "inputs=%d:normalize=%s", (int)num_sources, normalize ? "true" : "false");
|
||||
#else
|
||||
snprintf(args, sizeof(args), "inputs=%d", (int)num_sources);
|
||||
fprintf(stderr, "Warning: your ffmpeg version doesn't support disabling normalizing of mixed audio. Volume might be lower than expected\n");
|
||||
fprintf(stderr, "gsr warning: your ffmpeg version doesn't support disabling normalizing of mixed audio. Volume might be lower than expected\n");
|
||||
#endif
|
||||
|
||||
err = avfilter_graph_create_filter(&mix_ctx, mix_filter, "amix", args, NULL, filter_graph);
|
||||
@@ -1868,7 +1867,7 @@ static void info_command(void *userdata) {
|
||||
Display *dpy = XOpenDisplay(nullptr);
|
||||
if (!dpy) {
|
||||
wayland = true;
|
||||
fprintf(stderr, "Warning: failed to connect to the X server. Assuming wayland is running without Xwayland\n");
|
||||
fprintf(stderr, "gsr warning: failed to connect to the X server. Assuming wayland is running without Xwayland\n");
|
||||
}
|
||||
|
||||
XSetErrorHandler(x11_error_handler);
|
||||
@@ -1881,13 +1880,13 @@ static void info_command(void *userdata) {
|
||||
// Disable prime-run and similar options as it doesn't work, the monitor to capture has to be run on the same device.
|
||||
// This is fine on wayland since nvidia uses drm interface there and the monitor query checks the monitors connected
|
||||
// to the drm device.
|
||||
fprintf(stderr, "Warning: use of prime-run on X11 is not supported. Disabling prime-run\n");
|
||||
fprintf(stderr, "gsr warning: use of prime-run on X11 is not supported. Disabling prime-run\n");
|
||||
disable_prime_run();
|
||||
}
|
||||
|
||||
gsr_window *window = gsr_window_create(dpy, wayland);
|
||||
if(!window) {
|
||||
fprintf(stderr, "Error: failed to create window\n");
|
||||
fprintf(stderr, "gsr error: failed to create window\n");
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
@@ -1902,7 +1901,7 @@ static void info_command(void *userdata) {
|
||||
if(monitor_capture_use_drm(window, egl.gpu_info.vendor)) {
|
||||
// TODO: Allow specifying another card, and in other places
|
||||
if(!gsr_get_valid_card_path(&egl, egl.card_path, true)) {
|
||||
fprintf(stderr, "Error: no /dev/dri/cardX device found. Make sure that you have at least one monitor connected\n");
|
||||
fprintf(stderr, "gsr error: no /dev/dri/cardX device found. Make sure that you have at least one monitor connected\n");
|
||||
list_monitors = false;
|
||||
}
|
||||
}
|
||||
@@ -1983,7 +1982,7 @@ static void list_capture_options_command(const char *card_path, void *userdata)
|
||||
Display *dpy = XOpenDisplay(nullptr);
|
||||
if (!dpy) {
|
||||
wayland = true;
|
||||
fprintf(stderr, "Warning: failed to connect to the X server. Assuming wayland is running without Xwayland\n");
|
||||
fprintf(stderr, "gsr warning: failed to connect to the X server. Assuming wayland is running without Xwayland\n");
|
||||
}
|
||||
|
||||
XSetErrorHandler(x11_error_handler);
|
||||
@@ -1996,13 +1995,13 @@ static void list_capture_options_command(const char *card_path, void *userdata)
|
||||
// Disable prime-run and similar options as it doesn't work, the monitor to capture has to be run on the same device.
|
||||
// This is fine on wayland since nvidia uses drm interface there and the monitor query checks the monitors connected
|
||||
// to the drm device.
|
||||
fprintf(stderr, "Warning: use of prime-run on X11 is not supported. Disabling prime-run\n");
|
||||
fprintf(stderr, "gsr warning: use of prime-run on X11 is not supported. Disabling prime-run\n");
|
||||
disable_prime_run();
|
||||
}
|
||||
|
||||
gsr_window *window = gsr_window_create(dpy, wayland);
|
||||
if(!window) {
|
||||
fprintf(stderr, "Error: failed to create window\n");
|
||||
fprintf(stderr, "gsr error: failed to create window\n");
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
@@ -2020,7 +2019,7 @@ static void list_capture_options_command(const char *card_path, void *userdata)
|
||||
if(monitor_capture_use_drm(window, egl.gpu_info.vendor)) {
|
||||
// TODO: Allow specifying another card, and in other places
|
||||
if(!gsr_get_valid_card_path(&egl, egl.card_path, true)) {
|
||||
fprintf(stderr, "Error: no /dev/dri/cardX device found. Make sure that you have at least one monitor connected\n");
|
||||
fprintf(stderr, "gsr error: no /dev/dri/cardX device found. Make sure that you have at least one monitor connected\n");
|
||||
list_monitors = false;
|
||||
}
|
||||
}
|
||||
@@ -2053,7 +2052,7 @@ static std::string validate_monitor_get_valid(const gsr_egl *egl, const char* wi
|
||||
window_result = data.output_name;
|
||||
free(data.output_name);
|
||||
} else {
|
||||
fprintf(stderr, "Error: no usable output found\n");
|
||||
fprintf(stderr, "gsr error: no usable output found\n");
|
||||
_exit(51);
|
||||
}
|
||||
} else if(capture_use_drm || (strcmp(window_result.c_str(), "screen-direct") != 0 && strcmp(window_result.c_str(), "screen-direct-force") != 0)) {
|
||||
@@ -2123,7 +2122,7 @@ static gsr_capture* create_monitor_capture(const args_parser &arg_parser, gsr_eg
|
||||
const bool direct_capture = strcmp(arg_parser.window, "screen-direct") == 0 || strcmp(arg_parser.window, "screen-direct-force") == 0;
|
||||
if(direct_capture) {
|
||||
capture_target = "screen";
|
||||
fprintf(stderr, "Warning: %s capture option is not recommended unless you use G-SYNC as Nvidia has driver issues that can cause your system or games to freeze/crash.\n", arg_parser.window);
|
||||
fprintf(stderr, "gsr warning: %s capture option is not recommended unless you use G-SYNC as Nvidia has driver issues that can cause your system or games to freeze/crash.\n", arg_parser.window);
|
||||
}
|
||||
|
||||
gsr_capture_nvfbc_params nvfbc_params;
|
||||
@@ -2146,7 +2145,7 @@ static std::string region_get_data(gsr_egl *egl, vec2i *region_size, vec2i *regi
|
||||
if(window.empty()) {
|
||||
const bool is_x11 = gsr_window_get_display_server(egl->window) == GSR_DISPLAY_SERVER_X11;
|
||||
const gsr_connection_type connection_type = is_x11 ? GSR_CONNECTION_X11 : GSR_CONNECTION_DRM;
|
||||
fprintf(stderr, "Error: the region %dx%d+%d+%d doesn't match any monitor. Available monitors and their regions:\n", region_size->x, region_size->y, region_position->x, region_position->y);
|
||||
fprintf(stderr, "gsr error: the region %dx%d+%d+%d doesn't match any monitor. Available monitors and their regions:\n", region_size->x, region_size->y, region_position->x, region_position->y);
|
||||
|
||||
MonitorOutputCallbackUserdata userdata;
|
||||
userdata.window = egl->window;
|
||||
@@ -2173,12 +2172,12 @@ static gsr_capture* create_capture_impl(args_parser &arg_parser, gsr_egl *egl, b
|
||||
gsr_capture *capture = nullptr;
|
||||
if(strcmp(arg_parser.window, "focused") == 0) {
|
||||
if(wayland) {
|
||||
fprintf(stderr, "Error: GPU Screen Recorder window capture only works in a pure X11 session. Xwayland is not supported. You can record a monitor instead on wayland\n");
|
||||
fprintf(stderr, "gsr error: GPU Screen Recorder window capture only works in a pure X11 session. Xwayland is not supported. You can record a monitor instead on wayland\n");
|
||||
_exit(2);
|
||||
}
|
||||
|
||||
if(arg_parser.output_resolution.x <= 0 || arg_parser.output_resolution.y <= 0) {
|
||||
fprintf(stderr, "Error: invalid value for option -s '%dx%d' when using -w focused option. expected width and height to be greater than 0\n", arg_parser.output_resolution.x, arg_parser.output_resolution.y);
|
||||
fprintf(stderr, "gsr error: invalid value for option -s '%dx%d' when using -w focused option. expected width and height to be greater than 0\n", arg_parser.output_resolution.x, arg_parser.output_resolution.y);
|
||||
args_parser_print_usage();
|
||||
_exit(1);
|
||||
}
|
||||
@@ -2188,7 +2187,7 @@ static gsr_capture* create_capture_impl(args_parser &arg_parser, gsr_egl *egl, b
|
||||
#ifdef GSR_PORTAL
|
||||
// Desktop portal capture on x11 doesn't seem to be hardware accelerated
|
||||
if(!wayland) {
|
||||
fprintf(stderr, "Error: desktop portal capture is not supported on X11\n");
|
||||
fprintf(stderr, "gsr error: desktop portal capture is not supported on X11\n");
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
@@ -2202,7 +2201,7 @@ static gsr_capture* create_capture_impl(args_parser &arg_parser, gsr_egl *egl, b
|
||||
if(!capture)
|
||||
_exit(1);
|
||||
#else
|
||||
fprintf(stderr, "Error: option '-w portal' used but GPU Screen Recorder was compiled without desktop portal support. Please recompile GPU Screen recorder with the -Dportal=true option\n");
|
||||
fprintf(stderr, "gsr error: option '-w portal' used but GPU Screen Recorder was compiled without desktop portal support. Please recompile GPU Screen recorder with the -Dportal=true option\n");
|
||||
_exit(2);
|
||||
#endif
|
||||
} else if(strcmp(arg_parser.window, "region") == 0) {
|
||||
@@ -2219,14 +2218,14 @@ static gsr_capture* create_capture_impl(args_parser &arg_parser, gsr_egl *egl, b
|
||||
_exit(1);
|
||||
} else {
|
||||
if(wayland) {
|
||||
fprintf(stderr, "Error: GPU Screen Recorder window capture only works in a pure X11 session. Xwayland is not supported. You can record a monitor instead on wayland or use -w portal option which supports window capture if your wayland compositor supports window capture\n");
|
||||
fprintf(stderr, "gsr error: GPU Screen Recorder window capture only works in a pure X11 session. Xwayland is not supported. You can record a monitor instead on wayland or use -w portal option which supports window capture if your wayland compositor supports window capture\n");
|
||||
_exit(2);
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
src_window_id = strtol(arg_parser.window, nullptr, 0);
|
||||
if(src_window_id == None || errno == EINVAL) {
|
||||
fprintf(stderr, "Error: invalid window number %s\n", arg_parser.window);
|
||||
fprintf(stderr, "gsr error: invalid window number %s\n", arg_parser.window);
|
||||
args_parser_print_usage();
|
||||
_exit(1);
|
||||
}
|
||||
@@ -2402,13 +2401,13 @@ static std::vector<MergedAudioInputs> parse_audio_inputs(const AudioDevices &aud
|
||||
|
||||
if(request_audio_input.name == "default_output") {
|
||||
if(audio_devices.default_output.empty()) {
|
||||
fprintf(stderr, "Error: -a default_output was specified but no default audio output is specified in the audio server\n");
|
||||
fprintf(stderr, "gsr error: -a default_output was specified but no default audio output is specified in the audio server\n");
|
||||
_exit(2);
|
||||
}
|
||||
match = true;
|
||||
} else if(request_audio_input.name == "default_input") {
|
||||
if(audio_devices.default_input.empty()) {
|
||||
fprintf(stderr, "Error: -a default_input was specified but no default audio input is specified in the audio server\n");
|
||||
fprintf(stderr, "gsr error: -a default_input was specified but no default audio input is specified in the audio server\n");
|
||||
_exit(2);
|
||||
}
|
||||
match = true;
|
||||
@@ -2419,7 +2418,7 @@ static std::vector<MergedAudioInputs> parse_audio_inputs(const AudioDevices &aud
|
||||
}
|
||||
|
||||
if(!match) {
|
||||
fprintf(stderr, "Error: Audio device '%s' is not a valid audio device, expected one of:\n", request_audio_input.name.c_str());
|
||||
fprintf(stderr, "gsr error: Audio device '%s' is not a valid audio device, expected one of:\n", request_audio_input.name.c_str());
|
||||
if(!audio_devices.default_output.empty())
|
||||
fprintf(stderr, " default_output (Default output)\n");
|
||||
if(!audio_devices.default_input.empty())
|
||||
@@ -2503,7 +2502,7 @@ static gsr_audio_codec select_audio_codec_with_fallback(gsr_audio_codec audio_co
|
||||
if(file_extension == "webm") {
|
||||
//audio_codec_to_use = "opus";
|
||||
audio_codec = GSR_AUDIO_CODEC_OPUS;
|
||||
fprintf(stderr, "Warning: .webm files only support opus audio codec, changing audio codec from aac to opus\n");
|
||||
fprintf(stderr, "gsr warning: .webm files only support opus audio codec, changing audio codec from aac to opus\n");
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -2512,7 +2511,7 @@ static gsr_audio_codec select_audio_codec_with_fallback(gsr_audio_codec audio_co
|
||||
if(file_extension != "mp4" && file_extension != "mkv" && file_extension != "webm") {
|
||||
//audio_codec_to_use = "aac";
|
||||
audio_codec = GSR_AUDIO_CODEC_AAC;
|
||||
fprintf(stderr, "Warning: opus audio codec is only supported by .mp4, .mkv and .webm files, falling back to aac instead\n");
|
||||
fprintf(stderr, "gsr warning: opus audio codec is only supported by .mp4, .mkv and .webm files, falling back to aac instead\n");
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -2521,16 +2520,16 @@ static gsr_audio_codec select_audio_codec_with_fallback(gsr_audio_codec audio_co
|
||||
if(file_extension == "webm") {
|
||||
//audio_codec_to_use = "opus";
|
||||
audio_codec = GSR_AUDIO_CODEC_OPUS;
|
||||
fprintf(stderr, "Warning: .webm files only support opus audio codec, changing audio codec from flac to opus\n");
|
||||
fprintf(stderr, "gsr warning: .webm files only support opus audio codec, changing audio codec from flac to opus\n");
|
||||
} else if(file_extension != "mp4" && file_extension != "mkv") {
|
||||
//audio_codec_to_use = "aac";
|
||||
audio_codec = GSR_AUDIO_CODEC_AAC;
|
||||
fprintf(stderr, "Warning: flac audio codec is only supported by .mp4 and .mkv files, falling back to aac instead\n");
|
||||
fprintf(stderr, "gsr warning: flac audio codec is only supported by .mp4 and .mkv files, falling back to aac instead\n");
|
||||
} else if(uses_amix) {
|
||||
// TODO: remove this? is it true anymore?
|
||||
//audio_codec_to_use = "opus";
|
||||
audio_codec = GSR_AUDIO_CODEC_OPUS;
|
||||
fprintf(stderr, "Warning: flac audio codec is not supported when mixing audio sources, falling back to opus instead\n");
|
||||
fprintf(stderr, "gsr warning: flac audio codec is not supported when mixing audio sources, falling back to opus instead\n");
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -2561,7 +2560,7 @@ static const AVCodec* pick_video_codec(gsr_video_codec *video_codec, gsr_egl *eg
|
||||
|
||||
gsr_supported_video_codecs supported_video_codecs;
|
||||
if(!get_supported_video_codecs(egl, *video_codec, use_software_video_encoder, true, &supported_video_codecs)) {
|
||||
fprintf(stderr, "Error: failed to query for supported video codecs\n");
|
||||
fprintf(stderr, "gsr error: failed to query for supported video codecs\n");
|
||||
_exit(11);
|
||||
}
|
||||
|
||||
@@ -2631,7 +2630,7 @@ static const AVCodec* pick_video_codec(gsr_video_codec *video_codec, gsr_egl *eg
|
||||
if(!video_codec_auto && !video_codec_f && !is_flv) {
|
||||
switch(*video_codec) {
|
||||
case GSR_VIDEO_CODEC_H264: {
|
||||
fprintf(stderr, "Warning: selected video codec h264 is not supported, trying hevc instead\n");
|
||||
fprintf(stderr, "gsr warning: selected video codec h264 is not supported, trying hevc instead\n");
|
||||
*video_codec = GSR_VIDEO_CODEC_HEVC;
|
||||
if(supported_video_codecs.hevc.supported)
|
||||
video_codec_f = get_ffmpeg_video_codec(*video_codec, egl->gpu_info.vendor);
|
||||
@@ -2640,7 +2639,7 @@ static const AVCodec* pick_video_codec(gsr_video_codec *video_codec, gsr_egl *eg
|
||||
case GSR_VIDEO_CODEC_HEVC:
|
||||
case GSR_VIDEO_CODEC_HEVC_HDR:
|
||||
case GSR_VIDEO_CODEC_HEVC_10BIT: {
|
||||
fprintf(stderr, "Warning: selected video codec hevc is not supported, trying h264 instead\n");
|
||||
fprintf(stderr, "gsr warning: selected video codec hevc is not supported, trying h264 instead\n");
|
||||
*video_codec = GSR_VIDEO_CODEC_H264;
|
||||
if(supported_video_codecs.h264.supported)
|
||||
video_codec_f = get_ffmpeg_video_codec(*video_codec, egl->gpu_info.vendor);
|
||||
@@ -2649,7 +2648,7 @@ static const AVCodec* pick_video_codec(gsr_video_codec *video_codec, gsr_egl *eg
|
||||
case GSR_VIDEO_CODEC_AV1:
|
||||
case GSR_VIDEO_CODEC_AV1_HDR:
|
||||
case GSR_VIDEO_CODEC_AV1_10BIT: {
|
||||
fprintf(stderr, "Warning: selected video codec av1 is not supported, trying h264 instead\n");
|
||||
fprintf(stderr, "gsr warning: selected video codec av1 is not supported, trying h264 instead\n");
|
||||
*video_codec = GSR_VIDEO_CODEC_H264;
|
||||
if(supported_video_codecs.h264.supported)
|
||||
video_codec_f = get_ffmpeg_video_codec(*video_codec, egl->gpu_info.vendor);
|
||||
@@ -2660,11 +2659,11 @@ static const AVCodec* pick_video_codec(gsr_video_codec *video_codec, gsr_egl *eg
|
||||
// TODO: Cant fallback to other codec because webm only supports vp8/vp9
|
||||
break;
|
||||
case GSR_VIDEO_CODEC_H264_VULKAN: {
|
||||
fprintf(stderr, "Warning: selected video codec h264_vulkan is not supported, trying h264 instead\n");
|
||||
fprintf(stderr, "gsr warning: selected video codec h264_vulkan is not supported, trying h264 instead\n");
|
||||
*video_codec = GSR_VIDEO_CODEC_H264;
|
||||
// Need to do a query again because this time it's without vulkan
|
||||
if(!get_supported_video_codecs(egl, *video_codec, use_software_video_encoder, true, &supported_video_codecs)) {
|
||||
fprintf(stderr, "Error: failed to query for supported video codecs\n");
|
||||
fprintf(stderr, "gsr error: failed to query for supported video codecs\n");
|
||||
_exit(11);
|
||||
}
|
||||
if(supported_video_codecs.h264.supported)
|
||||
@@ -2672,11 +2671,11 @@ static const AVCodec* pick_video_codec(gsr_video_codec *video_codec, gsr_egl *eg
|
||||
break;
|
||||
}
|
||||
case GSR_VIDEO_CODEC_HEVC_VULKAN: {
|
||||
fprintf(stderr, "Warning: selected video codec hevc_vulkan is not supported, trying hevc instead\n");
|
||||
fprintf(stderr, "gsr warning: selected video codec hevc_vulkan is not supported, trying hevc instead\n");
|
||||
*video_codec = GSR_VIDEO_CODEC_HEVC;
|
||||
// Need to do a query again because this time it's without vulkan
|
||||
if(!get_supported_video_codecs(egl, *video_codec, use_software_video_encoder, true, &supported_video_codecs)) {
|
||||
fprintf(stderr, "Error: failed to query for supported video codecs\n");
|
||||
fprintf(stderr, "gsr error: failed to query for supported video codecs\n");
|
||||
_exit(11);
|
||||
}
|
||||
if(supported_video_codecs.hevc.supported)
|
||||
@@ -2688,7 +2687,7 @@ static const AVCodec* pick_video_codec(gsr_video_codec *video_codec, gsr_egl *eg
|
||||
|
||||
if(!video_codec_f) {
|
||||
const char *video_codec_name = video_codec_to_string(*video_codec);
|
||||
fprintf(stderr, "Error: your gpu does not support '%s' video codec. If you are sure that your gpu does support '%s' video encoding and you are using an AMD/Intel GPU,\n"
|
||||
fprintf(stderr, "gsr error: your gpu does not support '%s' video codec. If you are sure that your gpu does support '%s' video encoding and you are using an AMD/Intel GPU,\n"
|
||||
" then make sure you have installed the GPU specific vaapi packages (intel-media-driver, libva-intel-driver, libva-mesa-driver and linux-firmware).\n"
|
||||
" It's also possible that your distro has disabled hardware accelerated video encoding for '%s' video codec.\n"
|
||||
" This may be the case on corporate distros such as Manjaro, Fedora or OpenSUSE.\n"
|
||||
@@ -2710,10 +2709,10 @@ static const AVCodec* select_video_codec_with_fallback(gsr_video_codec *video_co
|
||||
const bool video_codec_auto = *video_codec == (gsr_video_codec)GSR_VIDEO_CODEC_AUTO;
|
||||
if(video_codec_auto) {
|
||||
if(strcmp(file_extension, "webm") == 0) {
|
||||
fprintf(stderr, "Info: using vp8 encoder because a codec was not specified and the file extension is .webm\n");
|
||||
fprintf(stderr, "gsr info: using vp8 encoder because a codec was not specified and the file extension is .webm\n");
|
||||
*video_codec = GSR_VIDEO_CODEC_VP8;
|
||||
} else {
|
||||
fprintf(stderr, "Info: using h264 encoder because a codec was not specified\n");
|
||||
fprintf(stderr, "gsr info: using h264 encoder because a codec was not specified\n");
|
||||
*video_codec = GSR_VIDEO_CODEC_H264;
|
||||
}
|
||||
}
|
||||
@@ -2723,13 +2722,13 @@ static const AVCodec* select_video_codec_with_fallback(gsr_video_codec *video_co
|
||||
if(is_flv) {
|
||||
if(*video_codec != GSR_VIDEO_CODEC_H264) {
|
||||
*video_codec = GSR_VIDEO_CODEC_H264;
|
||||
fprintf(stderr, "Warning: hevc/av1 is not compatible with flv, falling back to h264 instead.\n");
|
||||
fprintf(stderr, "gsr warning: hevc/av1 is not compatible with flv, falling back to h264 instead.\n");
|
||||
}
|
||||
|
||||
// if(audio_codec != GSR_AUDIO_CODEC_AAC) {
|
||||
// audio_codec_to_use = "aac";
|
||||
// audio_codec = GSR_AUDIO_CODEC_AAC;
|
||||
// fprintf(stderr, "Warning: flv only supports aac, falling back to aac instead.\n");
|
||||
// fprintf(stderr, "gsr warning: flv only supports aac, falling back to aac instead.\n");
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -2737,18 +2736,18 @@ static const AVCodec* select_video_codec_with_fallback(gsr_video_codec *video_co
|
||||
if(is_hls) {
|
||||
if(video_codec_is_av1(*video_codec)) {
|
||||
*video_codec = GSR_VIDEO_CODEC_HEVC;
|
||||
fprintf(stderr, "Warning: av1 is not compatible with hls (m3u8), falling back to hevc instead.\n");
|
||||
fprintf(stderr, "gsr warning: av1 is not compatible with hls (m3u8), falling back to hevc instead.\n");
|
||||
}
|
||||
|
||||
// if(audio_codec != GSR_AUDIO_CODEC_AAC) {
|
||||
// audio_codec_to_use = "aac";
|
||||
// audio_codec = GSR_AUDIO_CODEC_AAC;
|
||||
// fprintf(stderr, "Warning: hls (m3u8) only supports aac, falling back to aac instead.\n");
|
||||
// fprintf(stderr, "gsr warning: hls (m3u8) only supports aac, falling back to aac instead.\n");
|
||||
// }
|
||||
}
|
||||
|
||||
if(use_software_video_encoder && *video_codec != GSR_VIDEO_CODEC_H264) {
|
||||
fprintf(stderr, "Error: \"-encoder cpu\" option is currently only available when using h264 codec option (-k)\n");
|
||||
fprintf(stderr, "gsr error: \"-encoder cpu\" option is currently only available when using h264 codec option (-k)\n");
|
||||
args_parser_print_usage();
|
||||
_exit(1);
|
||||
}
|
||||
@@ -2774,7 +2773,7 @@ static std::vector<AudioDeviceData> create_device_audio_inputs(const std::vector
|
||||
} else {
|
||||
const std::string description = "gsr-" + audio_input.name;
|
||||
if(sound_device_get_by_name(&audio_device.sound_device, audio_input.name.c_str(), description.c_str(), num_channels, audio_codec_context->frame_size, audio_codec_context_get_audio_format(audio_codec_context)) != 0) {
|
||||
fprintf(stderr, "Error: failed to get \"%s\" audio device\n", audio_input.name.c_str());
|
||||
fprintf(stderr, "gsr error: failed to get \"%s\" audio device\n", audio_input.name.c_str());
|
||||
_exit(1);
|
||||
}
|
||||
}
|
||||
@@ -2809,7 +2808,7 @@ static AudioDeviceData create_application_audio_audio_input(const MergedAudioInp
|
||||
combined_sink_name += ".monitor";
|
||||
|
||||
if(sound_device_get_by_name(&audio_device.sound_device, combined_sink_name.c_str(), "gpu-screen-recorder", num_channels, audio_codec_context->frame_size, audio_codec_context_get_audio_format(audio_codec_context)) != 0) {
|
||||
fprintf(stderr, "Error: failed to setup audio recording to combined sink\n");
|
||||
fprintf(stderr, "gsr error: failed to setup audio recording to combined sink\n");
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
@@ -2867,7 +2866,7 @@ static bool get_image_format_from_filename(const char *filename, gsr_image_forma
|
||||
static bool av_open_file_write_header(AVFormatContext *av_format_context, const char *filename) {
|
||||
int ret = avio_open(&av_format_context->pb, filename, AVIO_FLAG_WRITE);
|
||||
if(ret < 0) {
|
||||
fprintf(stderr, "Error: Could not open '%s': %s\n", filename, av_error_to_string(ret));
|
||||
fprintf(stderr, "gsr error: Could not open '%s': %s\n", filename, av_error_to_string(ret));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2965,7 +2964,7 @@ int main(int argc, char **argv) {
|
||||
unsetenv("vblank_mode");
|
||||
|
||||
if(geteuid() == 0) {
|
||||
fprintf(stderr, "Error: don't run gpu-screen-recorder as the root user\n");
|
||||
fprintf(stderr, "gsr error: don't run gpu-screen-recorder as the root user\n");
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
@@ -3024,7 +3023,7 @@ int main(int argc, char **argv) {
|
||||
Display *dpy = XOpenDisplay(nullptr);
|
||||
if (!dpy) {
|
||||
wayland = true;
|
||||
fprintf(stderr, "Warning: failed to connect to the X server. Assuming wayland is running without Xwayland\n");
|
||||
fprintf(stderr, "gsr warning: failed to connect to the X server. Assuming wayland is running without Xwayland\n");
|
||||
}
|
||||
|
||||
XSetErrorHandler(x11_error_handler);
|
||||
@@ -3037,18 +3036,18 @@ int main(int argc, char **argv) {
|
||||
// Disable prime-run and similar options as it doesn't work, the monitor to capture has to be run on the same device.
|
||||
// This is fine on wayland since nvidia uses drm interface there and the monitor query checks the monitors connected
|
||||
// to the drm device.
|
||||
fprintf(stderr, "Warning: use of prime-run on X11 is not supported. Disabling prime-run\n");
|
||||
fprintf(stderr, "gsr warning: use of prime-run on X11 is not supported. Disabling prime-run\n");
|
||||
disable_prime_run();
|
||||
}
|
||||
|
||||
gsr_window *window = gsr_window_create(dpy, wayland);
|
||||
if(!window) {
|
||||
fprintf(stderr, "Error: failed to create window\n");
|
||||
fprintf(stderr, "gsr error: failed to create window\n");
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
if(is_portal_capture && is_using_prime_run()) {
|
||||
fprintf(stderr, "Warning: use of prime-run with -w portal option is currently not supported. Disabling prime-run\n");
|
||||
fprintf(stderr, "gsr warning: use of prime-run with -w portal option is currently not supported. Disabling prime-run\n");
|
||||
disable_prime_run();
|
||||
}
|
||||
|
||||
@@ -3071,7 +3070,7 @@ int main(int argc, char **argv) {
|
||||
if(monitor_capture_use_drm(window, egl.gpu_info.vendor)) {
|
||||
// TODO: Allow specifying another card, and in other places
|
||||
if(!gsr_get_valid_card_path(&egl, egl.card_path, is_monitor_capture)) {
|
||||
fprintf(stderr, "Error: no /dev/dri/cardX device found. Make sure that you have at least one monitor connected or record a single window instead on X11 or record with the -w portal option\n");
|
||||
fprintf(stderr, "gsr error: no /dev/dri/cardX device found. Make sure that you have at least one monitor connected or record a single window instead on X11 or record with the -w portal option\n");
|
||||
_exit(2);
|
||||
}
|
||||
}
|
||||
@@ -3084,7 +3083,7 @@ int main(int argc, char **argv) {
|
||||
gsr_image_format image_format;
|
||||
if(get_image_format_from_filename(arg_parser.filename, &image_format)) {
|
||||
if(audio_input_arg->num_values > 0) {
|
||||
fprintf(stderr, "Error: can't record audio (-a) when taking a screenshot\n");
|
||||
fprintf(stderr, "gsr error: can't record audio (-a) when taking a screenshot\n");
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
@@ -3097,9 +3096,9 @@ int main(int argc, char **argv) {
|
||||
avformat_alloc_output_context2(&av_format_context, nullptr, arg_parser.container_format, arg_parser.filename);
|
||||
if (!av_format_context) {
|
||||
if(arg_parser.container_format) {
|
||||
fprintf(stderr, "Error: Container format '%s' (argument -c) is not valid\n", arg_parser.container_format);
|
||||
fprintf(stderr, "gsr error: Container format '%s' (argument -c) is not valid\n", arg_parser.container_format);
|
||||
} else {
|
||||
fprintf(stderr, "Error: Failed to deduce container format from file extension. Use the '-c' option to specify container format\n");
|
||||
fprintf(stderr, "gsr error: Failed to deduce container format from file extension. Use the '-c' option to specify container format\n");
|
||||
args_parser_print_usage();
|
||||
_exit(1);
|
||||
}
|
||||
@@ -3128,7 +3127,7 @@ int main(int argc, char **argv) {
|
||||
// (Some?) livestreaming services require at least one audio track to work.
|
||||
// If not audio is provided then create one silent audio track.
|
||||
if(arg_parser.is_livestream && requested_audio_inputs.empty()) {
|
||||
fprintf(stderr, "Info: live streaming but no audio track was added. Adding a silent audio track\n");
|
||||
fprintf(stderr, "gsr info: live streaming but no audio track was added. Adding a silent audio track\n");
|
||||
MergedAudioInputs mai;
|
||||
mai.audio_inputs.push_back({""});
|
||||
requested_audio_inputs.push_back(std::move(mai));
|
||||
@@ -3147,7 +3146,7 @@ int main(int argc, char **argv) {
|
||||
|
||||
AVFrame *video_frame = av_frame_alloc();
|
||||
if(!video_frame) {
|
||||
fprintf(stderr, "Error: Failed to allocate video frame\n");
|
||||
fprintf(stderr, "gsr error: Failed to allocate video frame\n");
|
||||
_exit(1);
|
||||
}
|
||||
video_frame->format = video_codec_context->pix_fmt;
|
||||
@@ -3180,18 +3179,18 @@ int main(int argc, char **argv) {
|
||||
const size_t estimated_replay_buffer_packets = calculate_estimated_replay_buffer_packets(arg_parser.replay_buffer_size_secs, arg_parser.fps, arg_parser.audio_codec, requested_audio_inputs);
|
||||
gsr_encoder encoder;
|
||||
if(!gsr_encoder_init(&encoder, arg_parser.replay_storage, estimated_replay_buffer_packets, arg_parser.replay_buffer_size_secs, arg_parser.filename)) {
|
||||
fprintf(stderr, "Error: failed to create encoder\n");
|
||||
fprintf(stderr, "gsr error: failed to create encoder\n");
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
gsr_video_encoder *video_encoder = create_video_encoder(&egl, arg_parser);
|
||||
if(!video_encoder) {
|
||||
fprintf(stderr, "Error: failed to create video encoder\n");
|
||||
fprintf(stderr, "gsr error: failed to create video encoder\n");
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
if(!gsr_video_encoder_start(video_encoder, video_codec_context, video_frame)) {
|
||||
fprintf(stderr, "Error: failed to start video encoder\n");
|
||||
fprintf(stderr, "gsr error: failed to start video encoder\n");
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
@@ -3258,7 +3257,7 @@ int main(int argc, char **argv) {
|
||||
if(use_amix) {
|
||||
int err = init_filter_graph(audio_codec_context, &graph, &sink, src_filter_ctx, merged_audio_inputs.audio_inputs.size());
|
||||
if(err < 0) {
|
||||
fprintf(stderr, "Error: failed to create audio filter\n");
|
||||
fprintf(stderr, "gsr error: failed to create audio filter\n");
|
||||
_exit(1);
|
||||
}
|
||||
}
|
||||
@@ -3323,7 +3322,7 @@ int main(int argc, char **argv) {
|
||||
const size_t audio_buffer_size = audio_max_frame_size * 4 * 2; // max 4 bytes/sample, 2 channels
|
||||
uint8_t *empty_audio = (uint8_t*)malloc(audio_buffer_size);
|
||||
if(!empty_audio) {
|
||||
fprintf(stderr, "Error: failed to create empty audio\n");
|
||||
fprintf(stderr, "gsr error: failed to create empty audio\n");
|
||||
_exit(1);
|
||||
}
|
||||
memset(empty_audio, 0, audio_buffer_size);
|
||||
@@ -3429,7 +3428,7 @@ int main(int argc, char **argv) {
|
||||
if(audio_track.graph) {
|
||||
// TODO: av_buffersrc_add_frame
|
||||
if(av_buffersrc_write_frame(audio_device.src_filter_ctx, audio_device.frame) < 0) {
|
||||
fprintf(stderr, "Error: failed to add audio frame to filter\n");
|
||||
fprintf(stderr, "gsr error: failed to add audio frame to filter\n");
|
||||
}
|
||||
} else {
|
||||
ret = avcodec_send_frame(audio_track.codec_context, audio_device.frame);
|
||||
@@ -3463,7 +3462,7 @@ int main(int argc, char **argv) {
|
||||
if(audio_track.graph) {
|
||||
// TODO: av_buffersrc_add_frame
|
||||
if(av_buffersrc_write_frame(audio_device.src_filter_ctx, audio_device.frame) < 0) {
|
||||
fprintf(stderr, "Error: failed to add audio frame to filter\n");
|
||||
fprintf(stderr, "gsr error: failed to add audio frame to filter\n");
|
||||
}
|
||||
} else {
|
||||
ret = avcodec_send_frame(audio_track.codec_context, audio_device.frame);
|
||||
@@ -3656,7 +3655,7 @@ int main(int argc, char **argv) {
|
||||
// TODO: Move to separate thread because this could write to network (for example when livestreaming)
|
||||
gsr_encoder_receive_packets(&encoder, video_codec_context, video_frame->pts, VIDEO_STREAM_INDEX);
|
||||
} else {
|
||||
fprintf(stderr, "Error: avcodec_send_frame failed, error: %s\n", av_error_to_string(ret));
|
||||
fprintf(stderr, "gsr error: avcodec_send_frame failed, error: %s\n", av_error_to_string(ret));
|
||||
}
|
||||
|
||||
if(force_iframe_frame) {
|
||||
@@ -3684,7 +3683,7 @@ int main(int argc, char **argv) {
|
||||
|
||||
if(toggle_replay_recording && !arg_parser.replay_recording_directory) {
|
||||
toggle_replay_recording = 0;
|
||||
printf("Error: Unable to start recording since the -ro option was not specified\n");
|
||||
printf("gsr error: Unable to start recording since the -ro option was not specified\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
@@ -3711,7 +3710,7 @@ int main(int argc, char **argv) {
|
||||
force_iframe_frame = true;
|
||||
fprintf(stderr, "Started recording\n");
|
||||
} else {
|
||||
printf("Error: Failed to start recording\n");
|
||||
printf("gsr error: Failed to start recording\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
} else if(replay_recording_start_result.av_format_context) {
|
||||
@@ -3727,7 +3726,7 @@ int main(int argc, char **argv) {
|
||||
if(arg_parser.recording_saved_script)
|
||||
run_recording_saved_script_async(arg_parser.recording_saved_script, replay_recording_filepath.c_str(), "regular");
|
||||
} else {
|
||||
printf("Error: Failed to save recording\n");
|
||||
printf("gsr error: Failed to save recording\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
@@ -3740,7 +3739,7 @@ int main(int argc, char **argv) {
|
||||
if(save_replay_thread.valid() && save_replay_thread.wait_for(std::chrono::seconds(0)) == std::future_status::ready) {
|
||||
save_replay_thread.get();
|
||||
if(save_replay_output_filepath.empty()) {
|
||||
printf("Error: Failed to save replay\n");
|
||||
printf("gsr error: Failed to save replay\n");
|
||||
fflush(stdout);
|
||||
} else {
|
||||
puts(save_replay_output_filepath.c_str());
|
||||
@@ -3816,7 +3815,7 @@ int main(int argc, char **argv) {
|
||||
if(arg_parser.recording_saved_script)
|
||||
run_recording_saved_script_async(arg_parser.recording_saved_script, replay_recording_filepath.c_str(), "regular");
|
||||
} else {
|
||||
printf("Error: Failed to save recording\n");
|
||||
printf("gsr error: Failed to save recording\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
#include <pipewire/extensions/metadata.h>
|
||||
#include <pipewire/impl-module.h>
|
||||
|
||||
typedef struct {
|
||||
const gsr_pipewire_audio_port *output_port;
|
||||
const gsr_pipewire_audio_port *input_port;
|
||||
} gsr_pipewire_audio_desired_link;
|
||||
|
||||
static void on_core_info_cb(void *user_data, const struct pw_core_info *info) {
|
||||
gsr_pipewire_audio *self = user_data;
|
||||
//fprintf(stderr, "server name: %s\n", info->name);
|
||||
@@ -29,7 +34,7 @@ static const struct pw_core_events core_events = {
|
||||
};
|
||||
|
||||
static gsr_pipewire_audio_node* gsr_pipewire_audio_get_node_by_name_case_insensitive(gsr_pipewire_audio *self, const char *node_name, gsr_pipewire_audio_node_type node_type) {
|
||||
for(int i = 0; i < self->num_stream_nodes; ++i) {
|
||||
for(size_t i = 0; i < self->num_stream_nodes; ++i) {
|
||||
const gsr_pipewire_audio_node *node = &self->stream_nodes[i];
|
||||
if(node->type == node_type && strcasecmp(node->name, node_name) == 0)
|
||||
return &self->stream_nodes[i];
|
||||
@@ -38,7 +43,7 @@ static gsr_pipewire_audio_node* gsr_pipewire_audio_get_node_by_name_case_insensi
|
||||
}
|
||||
|
||||
static gsr_pipewire_audio_port* gsr_pipewire_audio_get_node_port_by_name(gsr_pipewire_audio *self, uint32_t node_id, const char *port_name) {
|
||||
for(int i = 0; i < self->num_ports; ++i) {
|
||||
for(size_t i = 0; i < self->num_ports; ++i) {
|
||||
if(self->ports[i].node_id == node_id && strcmp(self->ports[i].name, port_name) == 0)
|
||||
return &self->ports[i];
|
||||
}
|
||||
@@ -81,69 +86,68 @@ static void gsr_pipewire_get_node_input_port_by_type(gsr_pipewire_audio *self, c
|
||||
}
|
||||
}
|
||||
|
||||
static void gsr_pipewire_get_node_output_port_by_type(gsr_pipewire_audio *self, const gsr_pipewire_audio_node *output_node, gsr_pipewire_audio_node_type output_type,
|
||||
const gsr_pipewire_audio_port **output_fl_port, const gsr_pipewire_audio_port **output_fr_port)
|
||||
{
|
||||
*output_fl_port = NULL;
|
||||
*output_fr_port = NULL;
|
||||
|
||||
switch(output_type) {
|
||||
case GSR_PIPEWIRE_AUDIO_NODE_TYPE_STREAM_OUTPUT:
|
||||
*output_fl_port = gsr_pipewire_audio_get_node_port_by_name(self, output_node->id, "output_FL");
|
||||
*output_fr_port = gsr_pipewire_audio_get_node_port_by_name(self, output_node->id, "output_FR");
|
||||
break;
|
||||
case GSR_PIPEWIRE_AUDIO_NODE_TYPE_STREAM_INPUT:
|
||||
*output_fl_port = gsr_pipewire_audio_get_node_port_by_name(self, output_node->id, "monitor_FL");
|
||||
*output_fr_port = gsr_pipewire_audio_get_node_port_by_name(self, output_node->id, "monitor_FR");
|
||||
break;
|
||||
case GSR_PIPEWIRE_AUDIO_NODE_TYPE_SINK_OR_SOURCE: {
|
||||
*output_fl_port = gsr_pipewire_audio_get_node_port_by_name(self, output_node->id, "monitor_FL");
|
||||
*output_fr_port = gsr_pipewire_audio_get_node_port_by_name(self, output_node->id, "monitor_FR");
|
||||
if(!*output_fl_port || !*output_fr_port) {
|
||||
*output_fl_port = gsr_pipewire_audio_get_node_port_by_name(self, output_node->id, "capture_FL");
|
||||
*output_fr_port = gsr_pipewire_audio_get_node_port_by_name(self, output_node->id, "capture_FR");
|
||||
}
|
||||
if(!*output_fl_port || !*output_fr_port) {
|
||||
const gsr_pipewire_audio_port *output_mono_port = gsr_pipewire_audio_get_node_port_by_name(self, output_node->id, "monitor_MONO");
|
||||
if(!output_mono_port)
|
||||
output_mono_port = gsr_pipewire_audio_get_node_port_by_name(self, output_node->id, "capture_MONO");
|
||||
|
||||
if(output_mono_port) {
|
||||
*output_fl_port = output_mono_port;
|
||||
*output_fr_port = output_mono_port;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
static bool string_starts_with(const char *str, const char *substr) {
|
||||
const int len = strlen(str);
|
||||
const int substr_len = strlen(substr);
|
||||
return len >= substr_len && memcmp(str, substr, substr_len) == 0;
|
||||
}
|
||||
|
||||
static void gsr_pipewire_audio_establish_link(gsr_pipewire_audio *self, const gsr_pipewire_audio_port *input_fl_port, const gsr_pipewire_audio_port *input_fr_port,
|
||||
const gsr_pipewire_audio_port *output_fl_port, const gsr_pipewire_audio_port *output_fr_port)
|
||||
static bool string_ends_with(const char *str, const char *substr) {
|
||||
const int len = strlen(str);
|
||||
const int substr_len = strlen(substr);
|
||||
return len >= substr_len && memcmp(str + len - substr_len, substr, substr_len) == 0;
|
||||
}
|
||||
|
||||
/* Returns number of desired links */
|
||||
static size_t gsr_pipewire_get_node_output_ports(gsr_pipewire_audio *self, const gsr_pipewire_audio_node *output_node,
|
||||
gsr_pipewire_audio_desired_link *desired_links, size_t desired_links_max_size,
|
||||
const gsr_pipewire_audio_port *input_fl_port, const gsr_pipewire_audio_port *input_fr_port)
|
||||
{
|
||||
// TODO: Detect if link already exists before so we dont create these proxies when not needed
|
||||
size_t num_desired_links = 0;
|
||||
for(size_t i = 0; i < self->num_ports && num_desired_links < desired_links_max_size; ++i) {
|
||||
if(self->ports[i].node_id != output_node->id)
|
||||
continue;
|
||||
|
||||
if(string_starts_with(self->ports[i].name, "playback_"))
|
||||
continue;
|
||||
|
||||
if(string_ends_with(self->ports[i].name, "_MONO") || string_ends_with(self->ports[i].name, "_FC") || string_ends_with(self->ports[i].name, "_LFE")) {
|
||||
if(num_desired_links + 2 >= desired_links_max_size)
|
||||
break;
|
||||
|
||||
desired_links[num_desired_links + 0] = (gsr_pipewire_audio_desired_link){ .output_port = &self->ports[i], .input_port = input_fl_port };
|
||||
desired_links[num_desired_links + 1] = (gsr_pipewire_audio_desired_link){ .output_port = &self->ports[i], .input_port = input_fr_port };
|
||||
num_desired_links += 2;
|
||||
} else if(string_ends_with(self->ports[i].name, "_FL") || string_ends_with(self->ports[i].name, "_RL") || string_ends_with(self->ports[i].name, "_SL")) {
|
||||
if(num_desired_links + 1 >= desired_links_max_size)
|
||||
break;
|
||||
|
||||
desired_links[num_desired_links] = (gsr_pipewire_audio_desired_link){ .output_port = &self->ports[i], .input_port = input_fl_port };
|
||||
num_desired_links += 1;
|
||||
} else if(string_ends_with(self->ports[i].name, "_FR") || string_ends_with(self->ports[i].name, "_RR") || string_ends_with(self->ports[i].name, "_SR")) {
|
||||
if(num_desired_links + 1 >= desired_links_max_size)
|
||||
break;
|
||||
|
||||
desired_links[num_desired_links] = (gsr_pipewire_audio_desired_link){ .output_port = &self->ports[i], .input_port = input_fr_port };
|
||||
num_desired_links += 1;
|
||||
}
|
||||
}
|
||||
return num_desired_links;
|
||||
}
|
||||
|
||||
static void gsr_pipewire_audio_establish_link(gsr_pipewire_audio *self, const gsr_pipewire_audio_port *output_port, const gsr_pipewire_audio_port *input_port) {
|
||||
// TODO: Detect if link already exists before so we dont create these proxies when not needed.
|
||||
// We could do that by saving which nodes have been linked with which nodes after linking them.
|
||||
|
||||
//fprintf(stderr, "linking!\n");
|
||||
// TODO: error check and cleanup
|
||||
{
|
||||
struct pw_properties *props = pw_properties_new(NULL, NULL);
|
||||
pw_properties_setf(props, PW_KEY_LINK_OUTPUT_PORT, "%u", output_fl_port->id);
|
||||
pw_properties_setf(props, PW_KEY_LINK_INPUT_PORT, "%u", input_fl_port->id);
|
||||
// TODO: Clean this up when removing node
|
||||
struct pw_proxy *proxy = pw_core_create_object(self->core, "link-factory", PW_TYPE_INTERFACE_Link, PW_VERSION_LINK, &props->dict, 0);
|
||||
//self->server_version_sync = pw_core_sync(self->core, PW_ID_CORE, self->server_version_sync);
|
||||
pw_properties_free(props);
|
||||
}
|
||||
|
||||
{
|
||||
struct pw_properties *props = pw_properties_new(NULL, NULL);
|
||||
pw_properties_setf(props, PW_KEY_LINK_OUTPUT_PORT, "%u", output_fr_port->id);
|
||||
pw_properties_setf(props, PW_KEY_LINK_INPUT_PORT, "%u", input_fr_port->id);
|
||||
// TODO: Clean this up when removing node
|
||||
struct pw_proxy *proxy = pw_core_create_object(self->core, "link-factory", PW_TYPE_INTERFACE_Link, PW_VERSION_LINK, &props->dict, 0);
|
||||
//self->server_version_sync = pw_core_sync(self->core, PW_ID_CORE, self->server_version_sync);
|
||||
pw_properties_free(props);
|
||||
}
|
||||
struct pw_properties *props = pw_properties_new(NULL, NULL);
|
||||
pw_properties_setf(props, PW_KEY_LINK_OUTPUT_PORT, "%u", output_port->id);
|
||||
pw_properties_setf(props, PW_KEY_LINK_INPUT_PORT, "%u", input_port->id);
|
||||
// TODO: Clean this up when removing node
|
||||
struct pw_proxy *proxy = pw_core_create_object(self->core, "link-factory", PW_TYPE_INTERFACE_Link, PW_VERSION_LINK, &props->dict, 0);
|
||||
//self->server_version_sync = pw_core_sync(self->core, PW_ID_CORE, self->server_version_sync);
|
||||
pw_properties_free(props);
|
||||
}
|
||||
|
||||
static void gsr_pipewire_audio_create_link(gsr_pipewire_audio *self, const gsr_pipewire_audio_requested_link *requested_link) {
|
||||
@@ -158,7 +162,8 @@ static void gsr_pipewire_audio_create_link(gsr_pipewire_audio *self, const gsr_p
|
||||
if(!input_fl_port || !input_fr_port)
|
||||
return;
|
||||
|
||||
for(int i = 0; i < self->num_stream_nodes; ++i) {
|
||||
gsr_pipewire_audio_desired_link desired_links[64];
|
||||
for(size_t i = 0; i < self->num_stream_nodes; ++i) {
|
||||
const gsr_pipewire_audio_node *output_node = &self->stream_nodes[i];
|
||||
if(output_node->type != requested_link->output_type)
|
||||
continue;
|
||||
@@ -172,18 +177,15 @@ static void gsr_pipewire_audio_create_link(gsr_pipewire_audio *self, const gsr_p
|
||||
continue;
|
||||
}
|
||||
|
||||
const gsr_pipewire_audio_port *output_fl_port = NULL;
|
||||
const gsr_pipewire_audio_port *output_fr_port = NULL;
|
||||
gsr_pipewire_get_node_output_port_by_type(self, output_node, requested_link->output_type, &output_fl_port, &output_fr_port);
|
||||
if(!output_fl_port || !output_fr_port)
|
||||
continue;
|
||||
|
||||
gsr_pipewire_audio_establish_link(self, input_fl_port, input_fr_port, output_fl_port, output_fr_port);
|
||||
const size_t num_desired_links = gsr_pipewire_get_node_output_ports(self, output_node, desired_links, 64, input_fl_port, input_fr_port);
|
||||
for(size_t j = 0; j < num_desired_links; ++j) {
|
||||
gsr_pipewire_audio_establish_link(self, desired_links[j].output_port, desired_links[j].input_port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void gsr_pipewire_audio_create_links(gsr_pipewire_audio *self) {
|
||||
for(int i = 0; i < self->num_requested_links; ++i) {
|
||||
for(size_t i = 0; i < self->num_requested_links; ++i) {
|
||||
gsr_pipewire_audio_create_link(self, &self->requested_links[i]);
|
||||
}
|
||||
}
|
||||
@@ -214,24 +216,21 @@ static void gsr_pipewire_audio_create_link_for_default_devices(gsr_pipewire_audi
|
||||
if(!stream_output_node)
|
||||
return;
|
||||
|
||||
const gsr_pipewire_audio_port *output_fl_port = NULL;
|
||||
const gsr_pipewire_audio_port *output_fr_port = NULL;
|
||||
gsr_pipewire_get_node_output_port_by_type(self, stream_output_node, requested_link->output_type, &output_fl_port, &output_fr_port);
|
||||
if(!output_fl_port || !output_fr_port)
|
||||
return;
|
||||
|
||||
gsr_pipewire_audio_establish_link(self, input_fl_port, input_fr_port, output_fl_port, output_fr_port);
|
||||
//fprintf(stderr, "establishing a link from %u to %u\n", stream_output_node->id, stream_input_node->id);
|
||||
gsr_pipewire_audio_desired_link desired_links[64];
|
||||
const size_t num_desired_links = gsr_pipewire_get_node_output_ports(self, stream_output_node, desired_links, 64, input_fl_port, input_fr_port);
|
||||
for(size_t i = 0; i < num_desired_links; ++i) {
|
||||
gsr_pipewire_audio_establish_link(self, desired_links[i].output_port, desired_links[i].input_port);
|
||||
}
|
||||
}
|
||||
|
||||
static void gsr_pipewire_audio_create_links_for_default_devices(gsr_pipewire_audio *self, gsr_pipewire_audio_requested_type default_device_type) {
|
||||
for(int i = 0; i < self->num_requested_links; ++i) {
|
||||
for(size_t i = 0; i < self->num_requested_links; ++i) {
|
||||
gsr_pipewire_audio_create_link_for_default_devices(self, &self->requested_links[i], default_device_type);
|
||||
}
|
||||
}
|
||||
|
||||
static void gsr_pipewire_audio_destroy_links_by_output_to_input(gsr_pipewire_audio *self, uint32_t output_node_id, uint32_t input_node_id) {
|
||||
for(int i = 0; i < self->num_links; ++i) {
|
||||
for(size_t i = 0; i < self->num_links; ++i) {
|
||||
if(self->links[i].output_node_id == output_node_id && self->links[i].input_node_id == input_node_id)
|
||||
pw_registry_destroy(self->registry, self->links[i].id);
|
||||
}
|
||||
@@ -271,7 +270,7 @@ static void gsr_pipewire_destroy_default_device_link(gsr_pipewire_audio *self, c
|
||||
}
|
||||
|
||||
static void gsr_pipewire_destroy_default_device_links(gsr_pipewire_audio *self, gsr_pipewire_audio_requested_type default_device_type) {
|
||||
for(int i = 0; i < self->num_requested_links; ++i) {
|
||||
for(size_t i = 0; i < self->num_requested_links; ++i) {
|
||||
gsr_pipewire_destroy_default_device_link(self, &self->requested_links[i], default_device_type);
|
||||
}
|
||||
}
|
||||
@@ -370,6 +369,24 @@ static bool gsr_pipewire_audio_listen_on_metadata(gsr_pipewire_audio *self, uint
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool array_ensure_capacity(void **array, size_t size, size_t *capacity_items, size_t element_size) {
|
||||
if(size + 1 >= *capacity_items) {
|
||||
size_t new_capacity_items = *capacity_items * 2;
|
||||
if(new_capacity_items == 0)
|
||||
new_capacity_items = 32;
|
||||
|
||||
void *new_data = realloc(*array, new_capacity_items * element_size);
|
||||
if(!new_data) {
|
||||
fprintf(stderr, "gsr error: pipewire_audio: failed to reallocate memory\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
*array = new_data;
|
||||
*capacity_items = new_capacity_items;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void registry_event_global(void *data, uint32_t id, uint32_t permissions,
|
||||
const char *type, uint32_t version,
|
||||
const struct spa_dict *props)
|
||||
@@ -389,11 +406,14 @@ static void registry_event_global(void *data, uint32_t id, uint32_t permissions,
|
||||
const bool is_stream_input = media_class && strcmp(media_class, "Stream/Input/Audio") == 0;
|
||||
const bool is_sink = media_class && strcmp(media_class, "Audio/Sink") == 0;
|
||||
const bool is_source = media_class && strcmp(media_class, "Audio/Source") == 0;
|
||||
if(self->num_stream_nodes < GSR_PIPEWIRE_AUDIO_MAX_STREAM_NODES && node_name && (is_stream_output || is_stream_input || is_sink || is_source)) {
|
||||
if(node_name && (is_stream_output || is_stream_input || is_sink || is_source)) {
|
||||
//const char *application_binary = spa_dict_lookup(props, PW_KEY_APP_PROCESS_BINARY);
|
||||
//const char *application_name = spa_dict_lookup(props, PW_KEY_APP_NAME);
|
||||
//fprintf(stderr, " node name: %s, app binary: %s, app name: %s\n", node_name, application_binary, application_name);
|
||||
|
||||
if(!array_ensure_capacity((void**)&self->stream_nodes, self->num_stream_nodes, &self->stream_nodes_capacity_items, sizeof(gsr_pipewire_audio_node)))
|
||||
return;
|
||||
|
||||
char *node_name_copy = strdup(node_name);
|
||||
if(node_name_copy) {
|
||||
self->stream_nodes[self->num_stream_nodes].id = id;
|
||||
@@ -408,8 +428,6 @@ static void registry_event_global(void *data, uint32_t id, uint32_t permissions,
|
||||
|
||||
gsr_pipewire_audio_create_links(self);
|
||||
}
|
||||
} else if(self->num_stream_nodes >= GSR_PIPEWIRE_AUDIO_MAX_STREAM_NODES) {
|
||||
fprintf(stderr, "gsr error: reached the maximum amount of audio stream nodes\n");
|
||||
}
|
||||
} else if(strcmp(type, PW_TYPE_INTERFACE_Port) == 0) {
|
||||
const char *port_name = spa_dict_lookup(props, PW_KEY_PORT_NAME);
|
||||
@@ -424,7 +442,10 @@ static void registry_event_global(void *data, uint32_t id, uint32_t permissions,
|
||||
const char *node_id = spa_dict_lookup(props, PW_KEY_NODE_ID);
|
||||
const int node_id_num = node_id ? atoi(node_id) : 0;
|
||||
|
||||
if(self->num_ports < GSR_PIPEWIRE_AUDIO_MAX_PORTS && port_name && direction >= 0 && node_id_num > 0) {
|
||||
if(port_name && direction >= 0 && node_id_num > 0) {
|
||||
if(!array_ensure_capacity((void**)&self->ports, self->num_ports, &self->ports_capacity_items, sizeof(gsr_pipewire_audio_port)))
|
||||
return;
|
||||
|
||||
//fprintf(stderr, " port name: %s, node id: %d, direction: %s\n", port_name, node_id_num, port_direction);
|
||||
char *port_name_copy = strdup(port_name);
|
||||
if(port_name_copy) {
|
||||
@@ -437,8 +458,6 @@ static void registry_event_global(void *data, uint32_t id, uint32_t permissions,
|
||||
|
||||
gsr_pipewire_audio_create_links(self);
|
||||
}
|
||||
} else if(self->num_ports >= GSR_PIPEWIRE_AUDIO_MAX_PORTS) {
|
||||
fprintf(stderr, "gsr error: reached the maximum amount of audio ports\n");
|
||||
}
|
||||
} else if(strcmp(type, PW_TYPE_INTERFACE_Link) == 0) {
|
||||
const char *output_node = spa_dict_lookup(props, PW_KEY_LINK_OUTPUT_NODE);
|
||||
@@ -446,14 +465,15 @@ static void registry_event_global(void *data, uint32_t id, uint32_t permissions,
|
||||
|
||||
const uint32_t output_node_id_num = output_node ? atoi(output_node) : 0;
|
||||
const uint32_t input_node_id_num = input_node ? atoi(input_node) : 0;
|
||||
if(self->num_links < GSR_PIPEWIRE_AUDIO_MAX_LINKS && output_node_id_num > 0 && input_node_id_num > 0) {
|
||||
if(output_node_id_num > 0 && input_node_id_num > 0) {
|
||||
if(!array_ensure_capacity((void**)&self->links, self->num_links, &self->links_capacity_items, sizeof(gsr_pipewire_audio_link)))
|
||||
return;
|
||||
|
||||
//fprintf(stderr, " new link (%u): %u -> %u\n", id, output_node_id_num, input_node_id_num);
|
||||
self->links[self->num_links].id = id;
|
||||
self->links[self->num_links].output_node_id = output_node_id_num;
|
||||
self->links[self->num_links].input_node_id = input_node_id_num;
|
||||
++self->num_links;
|
||||
} else if(self->num_ports >= GSR_PIPEWIRE_AUDIO_MAX_LINKS) {
|
||||
fprintf(stderr, "gsr error: reached the maximum amount of audio links\n");
|
||||
}
|
||||
} else if(strcmp(type, PW_TYPE_INTERFACE_Metadata) == 0) {
|
||||
const char *name = spa_dict_lookup(props, PW_KEY_METADATA_NAME);
|
||||
@@ -463,7 +483,7 @@ static void registry_event_global(void *data, uint32_t id, uint32_t permissions,
|
||||
}
|
||||
|
||||
static bool gsr_pipewire_audio_remove_node_by_id(gsr_pipewire_audio *self, uint32_t node_id) {
|
||||
for(int i = 0; i < self->num_stream_nodes; ++i) {
|
||||
for(size_t i = 0; i < self->num_stream_nodes; ++i) {
|
||||
if(self->stream_nodes[i].id != node_id)
|
||||
continue;
|
||||
|
||||
@@ -476,7 +496,7 @@ static bool gsr_pipewire_audio_remove_node_by_id(gsr_pipewire_audio *self, uint3
|
||||
}
|
||||
|
||||
static bool gsr_pipewire_audio_remove_port_by_id(gsr_pipewire_audio *self, uint32_t port_id) {
|
||||
for(int i = 0; i < self->num_ports; ++i) {
|
||||
for(size_t i = 0; i < self->num_ports; ++i) {
|
||||
if(self->ports[i].id != port_id)
|
||||
continue;
|
||||
|
||||
@@ -489,7 +509,7 @@ static bool gsr_pipewire_audio_remove_port_by_id(gsr_pipewire_audio *self, uint3
|
||||
}
|
||||
|
||||
static bool gsr_pipewire_audio_remove_link_by_id(gsr_pipewire_audio *self, uint32_t link_id) {
|
||||
for(int i = 0; i < self->num_links; ++i) {
|
||||
for(size_t i = 0; i < self->num_links; ++i) {
|
||||
if(self->links[i].id != link_id)
|
||||
continue;
|
||||
|
||||
@@ -580,13 +600,19 @@ void gsr_pipewire_audio_deinit(gsr_pipewire_audio *self) {
|
||||
pw_thread_loop_stop(self->thread_loop);
|
||||
}
|
||||
|
||||
for(int i = 0; i < self->num_virtual_sink_proxies; ++i) {
|
||||
for(size_t i = 0; i < self->num_virtual_sink_proxies; ++i) {
|
||||
if(self->virtual_sink_proxies[i]) {
|
||||
pw_proxy_destroy(self->virtual_sink_proxies[i]);
|
||||
self->virtual_sink_proxies[i] = NULL;
|
||||
}
|
||||
}
|
||||
self->num_virtual_sink_proxies = 0;
|
||||
self->virtual_sink_proxies_capacity_items = 0;
|
||||
|
||||
if(self->virtual_sink_proxies) {
|
||||
free(self->virtual_sink_proxies);
|
||||
self->virtual_sink_proxies = NULL;
|
||||
}
|
||||
|
||||
if(self->metadata_proxy) {
|
||||
spa_hook_remove(&self->metadata_listener);
|
||||
@@ -615,26 +641,50 @@ void gsr_pipewire_audio_deinit(gsr_pipewire_audio *self) {
|
||||
self->thread_loop = NULL;
|
||||
}
|
||||
|
||||
for(int i = 0; i < self->num_stream_nodes; ++i) {
|
||||
free(self->stream_nodes[i].name);
|
||||
}
|
||||
self->num_stream_nodes = 0;
|
||||
|
||||
for(int i = 0; i < self->num_ports; ++i) {
|
||||
free(self->ports[i].name);
|
||||
}
|
||||
self->num_ports = 0;
|
||||
|
||||
self->num_links = 0;
|
||||
|
||||
for(int i = 0; i < self->num_requested_links; ++i) {
|
||||
for(int j = 0; j < self->requested_links[i].num_outputs; ++j) {
|
||||
free(self->requested_links[i].outputs[j].name);
|
||||
if(self->stream_nodes) {
|
||||
for(size_t i = 0; i < self->num_stream_nodes; ++i) {
|
||||
free(self->stream_nodes[i].name);
|
||||
}
|
||||
free(self->requested_links[i].outputs);
|
||||
free(self->requested_links[i].input_name);
|
||||
self->num_stream_nodes = 0;
|
||||
self->stream_nodes_capacity_items = 0;
|
||||
|
||||
free(self->stream_nodes);
|
||||
self->stream_nodes = NULL;
|
||||
}
|
||||
|
||||
if(self->ports) {
|
||||
for(size_t i = 0; i < self->num_ports; ++i) {
|
||||
free(self->ports[i].name);
|
||||
}
|
||||
self->num_ports = 0;
|
||||
self->ports_capacity_items = 0;
|
||||
|
||||
free(self->ports);
|
||||
self->ports = NULL;
|
||||
}
|
||||
|
||||
if(self->links) {
|
||||
self->num_links = 0;
|
||||
self->links_capacity_items = 0;
|
||||
|
||||
free(self->links);
|
||||
self->links = NULL;
|
||||
}
|
||||
|
||||
if(self->requested_links) {
|
||||
for(size_t i = 0; i < self->num_requested_links; ++i) {
|
||||
for(int j = 0; j < self->requested_links[i].num_outputs; ++j) {
|
||||
free(self->requested_links[i].outputs[j].name);
|
||||
}
|
||||
free(self->requested_links[i].outputs);
|
||||
free(self->requested_links[i].input_name);
|
||||
}
|
||||
self->num_requested_links = 0;
|
||||
self->requested_links_capacity_items = 0;
|
||||
|
||||
free(self->requested_links);
|
||||
self->requested_links = NULL;
|
||||
}
|
||||
self->num_requested_links = 0;
|
||||
|
||||
#if PW_CHECK_VERSION(0, 3, 49)
|
||||
pw_deinit();
|
||||
@@ -653,10 +703,8 @@ static struct pw_properties* gsr_pipewire_create_null_audio_sink(const char *nam
|
||||
}
|
||||
|
||||
bool gsr_pipewire_audio_create_virtual_sink(gsr_pipewire_audio *self, const char *name) {
|
||||
if(self->num_virtual_sink_proxies == GSR_PIPEWIRE_AUDIO_MAX_VIRTUAL_SINKS) {
|
||||
fprintf(stderr, "gsr error: gsr_pipewire_audio_create_virtual_sink: reached max number of virtual sinks\n");
|
||||
if(!array_ensure_capacity((void**)&self->virtual_sink_proxies, self->num_virtual_sink_proxies, &self->virtual_sink_proxies_capacity_items, sizeof(struct pw_proxy*)))
|
||||
return false;
|
||||
}
|
||||
|
||||
pw_thread_loop_lock(self->thread_loop);
|
||||
|
||||
@@ -701,10 +749,8 @@ static bool string_remove_suffix(char *str, const char *suffix) {
|
||||
}
|
||||
|
||||
static bool gsr_pipewire_audio_add_links_to_output(gsr_pipewire_audio *self, const char **output_names, int num_output_names, const char *input_name, gsr_pipewire_audio_node_type output_type, gsr_pipewire_audio_link_input_type input_type, bool inverted) {
|
||||
if(self->num_requested_links >= GSR_PIPEWIRE_AUDIO_MAX_REQUESTED_LINKS) {
|
||||
fprintf(stderr, "gsr error: reached the maximum amount of audio links\n");
|
||||
if(!array_ensure_capacity((void**)&self->requested_links, self->num_requested_links, &self->requested_links_capacity_items, sizeof(gsr_pipewire_audio_requested_link)))
|
||||
return false;
|
||||
}
|
||||
|
||||
gsr_pipewire_audio_requested_output *outputs = calloc(num_output_names, sizeof(gsr_pipewire_audio_requested_output));
|
||||
if(!outputs)
|
||||
@@ -781,7 +827,7 @@ bool gsr_pipewire_audio_add_link_from_sources_to_sink(gsr_pipewire_audio *self,
|
||||
|
||||
void gsr_pipewire_audio_for_each_app(gsr_pipewire_audio *self, gsr_pipewire_audio_app_query_callback callback, void *userdata) {
|
||||
pw_thread_loop_lock(self->thread_loop);
|
||||
for(int i = 0; i < self->num_stream_nodes; ++i) {
|
||||
for(int i = 0; i < (int)self->num_stream_nodes; ++i) {
|
||||
const gsr_pipewire_audio_node *node = &self->stream_nodes[i];
|
||||
if(node->type != GSR_PIPEWIRE_AUDIO_NODE_TYPE_STREAM_OUTPUT)
|
||||
continue;
|
||||
|
||||
@@ -154,7 +154,7 @@ static bool startup_get_default_devices(pa_handle *p, const char *device_name) {
|
||||
}
|
||||
|
||||
if(p->default_output_device_name[0] == '\0') {
|
||||
fprintf(stderr, "Error: failed to find default audio output device\n");
|
||||
fprintf(stderr, "gsr error: failed to find default audio output device\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -197,7 +197,7 @@ static pa_handle* pa_sound_device_new(const char *server,
|
||||
const int buffer_size = attr->fragsize;
|
||||
void *buffer = malloc(buffer_size);
|
||||
if(!buffer) {
|
||||
fprintf(stderr, "Error: failed to allocate buffer for audio\n");
|
||||
fprintf(stderr, "gsr error: failed to allocate buffer for audio\n");
|
||||
*rerror = -1;
|
||||
return NULL;
|
||||
}
|
||||
@@ -426,7 +426,7 @@ int sound_device_get_by_name(SoundDevice *device, const char *device_name, const
|
||||
int error = 0;
|
||||
pa_handle *handle = pa_sound_device_new(nullptr, description, device_name, description, &ss, &buffer_attr, &error);
|
||||
if(!handle) {
|
||||
fprintf(stderr, "Error: pa_sound_device_new() failed: %s. Audio input device %s might not be valid\n", pa_strerror(error), device_name);
|
||||
fprintf(stderr, "gsr error: pa_sound_device_new() failed: %s. Audio input device %s might not be valid\n", pa_strerror(error), device_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user