aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--include/utils.h18
-rw-r--r--src/main.cpp476
-rw-r--r--src/pipewire_audio.c29
-rw-r--r--src/utils.c86
4 files changed, 327 insertions, 282 deletions
diff --git a/include/utils.h b/include/utils.h
index d2c3b7d..d1a2a3c 100644
--- a/include/utils.h
+++ b/include/utils.h
@@ -6,6 +6,7 @@
#include "../include/defs.h"
#include <stdbool.h>
#include <stdint.h>
+#include <stddef.h>
typedef struct AVCodecContext AVCodecContext;
typedef struct AVFrame AVFrame;
@@ -64,4 +65,21 @@ unsigned int gl_create_texture(gsr_egl *egl, int width, int height, int internal
bool get_nvidia_driver_version(int *major, int *minor);
+/* Return false from the callback to stop splitting */
+typedef bool (*gsr_string_split_callback)(const char *str, size_t size, void *userdata);
+void gsr_string_split(const char *str, char delimiter, gsr_string_split_callback callback, void *userdata);
+
+bool gsr_string_starts_with(const char *str, size_t str_size, const char *substr);
+bool gsr_string_ends_with(const char *str, const char *substr);
+
+bool gsr_string_to_int(const char *str, size_t size, int *number);
+bool gsr_string_to_int64(const char *str, size_t size, int64_t *number);
+
+bool gsr_array_ensure_capacity(void **array, size_t num_items, size_t *capacity_items, size_t item_size);
+
+/* These write a formatted local date/time into |str|, which should be at least 128 bytes in size */
+void gsr_get_date_str(char *str, size_t size);
+void gsr_get_date_only_str(char *str, size_t size);
+void gsr_get_time_only_str(char *str, size_t size);
+
#endif /* GSR_UTILS_H */
diff --git a/src/main.cpp b/src/main.cpp
index df609bb..a2933f3 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -781,30 +781,6 @@ static void save_replay_30_minutes_handler(int) {
save_replay_seconds = 60*30;
}
-static std::string get_date_str() {
- char str[128];
- time_t now = time(NULL);
- struct tm *t = localtime(&now);
- strftime(str, sizeof(str)-1, "%Y-%m-%d_%H-%M-%S", t);
- return str;
-}
-
-static std::string get_date_only_str() {
- char str[128];
- time_t now = time(NULL);
- struct tm *t = localtime(&now);
- strftime(str, sizeof(str)-1, "%Y-%m-%d", t);
- return str;
-}
-
-static std::string get_time_only_str() {
- char str[128];
- time_t now = time(NULL);
- struct tm *t = localtime(&now);
- strftime(str, sizeof(str)-1, "%H-%M-%S", t);
- return str;
-}
-
static AVStream* create_stream(AVFormatContext *av_format_context, AVCodecContext *codec_context) {
AVStream *stream = avformat_new_stream(av_format_context, nullptr);
if (!stream) {
@@ -1082,16 +1058,20 @@ static std::future<bool> save_replay_thread;
static std::string save_replay_output_filepath;
static std::string create_new_recording_filepath_from_timestamp(std::string directory, const char *filename_prefix, const std::string &file_extension, bool date_folders) {
+ char date_str[128];
std::string output_filepath;
if(date_folders) {
- std::string output_folder = directory + '/' + get_date_only_str();
+ gsr_get_date_only_str(date_str, sizeof(date_str));
+ std::string output_folder = directory + '/' + date_str;
if(create_directory_recursive(&output_folder[0]) != 0)
gsr_log(GSR_LOG_LEVEL_ERROR, "failed to create directory: %s", output_folder.c_str());
- output_filepath = output_folder + "/" + filename_prefix + "_" + get_time_only_str() + "." + file_extension;
+ gsr_get_time_only_str(date_str, sizeof(date_str));
+ output_filepath = output_folder + "/" + filename_prefix + "_" + date_str + "." + file_extension;
} else {
if(create_directory_recursive(&directory[0]) != 0)
gsr_log(GSR_LOG_LEVEL_ERROR, "failed to create directory: %s", directory.c_str());
- output_filepath = directory + "/" + filename_prefix + "_" + get_date_str() + "." + file_extension;
+ gsr_get_date_str(date_str, sizeof(date_str));
+ output_filepath = directory + "/" + filename_prefix + "_" + date_str + "." + file_extension;
}
return output_filepath;
}
@@ -1240,35 +1220,6 @@ static bool save_replay_async(AVCodecContext *video_codec_context, int video_str
return true;
}
-static void split_string(const std::string &str, char delimiter, std::function<bool(const char*,size_t)> callback) {
- size_t index = 0;
- while(index < str.size()) {
- size_t end_index = str.find(delimiter, index);
- if(end_index == std::string::npos)
- end_index = str.size();
-
- if(!callback(&str[index], end_index - index))
- break;
-
- index = end_index + 1;
- }
-}
-
-static bool string_starts_with(const char *str, size_t str_size, const char *substr) {
- int len = strlen(substr);
- return (int)str_size >= len && memcmp(str, substr, len) == 0;
-}
-
-static bool string_starts_with(const std::string &str, const char *substr) {
- return string_starts_with(str.data(), str.size(), substr);
-}
-
-static bool string_ends_with(const char *str, const char *substr) {
- int str_len = strlen(str);
- int substr_len = strlen(substr);
- return str_len >= substr_len && memcmp(str + str_len - substr_len, substr, substr_len) == 0;
-}
-
static const AudioDevice* get_audio_device_by_name(const std::vector<AudioDevice> &audio_devices, const char *name) {
for(const auto &audio_device : audio_devices) {
if(strcmp(audio_device.name.c_str(), name) == 0)
@@ -1277,44 +1228,42 @@ static const AudioDevice* get_audio_device_by_name(const std::vector<AudioDevice
return nullptr;
}
-static MergedAudioInputs parse_audio_input_arg(const char *str) {
- MergedAudioInputs result;
- result.track_name = str;
-
- split_string(str, '|', [&](const char *sub, size_t size) {
- if(size == 0)
- return true;
+static bool parse_audio_input_arg_callback(const char *sub, size_t size, void *userdata) {
+ MergedAudioInputs *result = (MergedAudioInputs*)userdata;
+ if(size == 0)
+ return true;
- AudioInput audio_input;
- audio_input.name.assign(sub, size);
+ AudioInput audio_input;
+ audio_input.name.assign(sub, size);
+
+ if(gsr_string_starts_with(audio_input.name.data(), audio_input.name.size(), "name:")) {
+ result->custom_name = audio_input.name.substr(5);
+ } else if(gsr_string_starts_with(audio_input.name.data(), audio_input.name.size(), "app:")) {
+ audio_input.name.erase(audio_input.name.begin(), audio_input.name.begin() + 4);
+ audio_input.type = AudioInputType::APPLICATION;
+ audio_input.inverted = false;
+ result->audio_inputs.push_back(std::move(audio_input));
+ } else if(gsr_string_starts_with(audio_input.name.data(), audio_input.name.size(), "app-inverse:")) {
+ audio_input.name.erase(audio_input.name.begin(), audio_input.name.begin() + 12);
+ audio_input.type = AudioInputType::APPLICATION;
+ audio_input.inverted = true;
+ result->audio_inputs.push_back(std::move(audio_input));
+ } else if(gsr_string_starts_with(audio_input.name.data(), audio_input.name.size(), "device:")) {
+ audio_input.name.erase(audio_input.name.begin(), audio_input.name.begin() + 7);
+ audio_input.type = AudioInputType::DEVICE;
+ result->audio_inputs.push_back(std::move(audio_input));
+ } else {
+ audio_input.type = AudioInputType::DEVICE;
+ result->audio_inputs.push_back(std::move(audio_input));
+ }
- if(string_starts_with(audio_input.name.c_str(), "name:")) {
- result.custom_name = audio_input.name.substr(5);
- return true;
- } else if(string_starts_with(audio_input.name.c_str(), "app:")) {
- audio_input.name.erase(audio_input.name.begin(), audio_input.name.begin() + 4);
- audio_input.type = AudioInputType::APPLICATION;
- audio_input.inverted = false;
- result.audio_inputs.push_back(std::move(audio_input));
- return true;
- } else if(string_starts_with(audio_input.name.c_str(), "app-inverse:")) {
- audio_input.name.erase(audio_input.name.begin(), audio_input.name.begin() + 12);
- audio_input.type = AudioInputType::APPLICATION;
- audio_input.inverted = true;
- result.audio_inputs.push_back(std::move(audio_input));
- return true;
- } else if(string_starts_with(audio_input.name.c_str(), "device:")) {
- audio_input.name.erase(audio_input.name.begin(), audio_input.name.begin() + 7);
- audio_input.type = AudioInputType::DEVICE;
- result.audio_inputs.push_back(std::move(audio_input));
- return true;
- } else {
- audio_input.type = AudioInputType::DEVICE;
- result.audio_inputs.push_back(std::move(audio_input));
- return true;
- }
- });
+ return true;
+}
+static MergedAudioInputs parse_audio_input_arg(const char *str) {
+ MergedAudioInputs result;
+ result.track_name = str;
+ gsr_string_split(str, '|', parse_audio_input_arg_callback, &result);
return result;
}
@@ -2692,16 +2641,6 @@ static bool contains_non_hex_number(const char *str) {
return is_hex && !hex_start;
}
-template <typename T>
-static bool string_to_int(const char *str, size_t len, T *number) {
- char number_str[32];
- snprintf(number_str, sizeof(number_str), "%.*s", (int)len, str);
-
- errno = 0;
- *number = strtol(number_str, NULL, 0);
- return errno == 0;
-}
-
static void capture_source_type_from_string(const char *capture_source_str, size_t size, CaptureSource &capture_source) {
char capture_source_str_n[64];
snprintf(capture_source_str_n, sizeof(capture_source_str_n), "%.*s", (int)size, capture_source_str);
@@ -2766,180 +2705,199 @@ static bool string_to_bool(const char *str, size_t len, bool *value) {
}
}
-static void parse_capture_source_options(const std::string &capture_source_str, CaptureSource &capture_source) {
- bool is_first_column = true;
+struct ParseCaptureSourceOptionsUserdata {
+ CaptureSource *capture_source;
+ bool is_first_column;
+};
- split_string(capture_source_str, ';', [&](const char *sub, size_t size) {
- if(size == 0)
- return true;
+static bool parse_capture_source_options_callback(const char *sub, size_t size, void *userdata) {
+ ParseCaptureSourceOptionsUserdata *parse_userdata = (ParseCaptureSourceOptionsUserdata*)userdata;
+ CaptureSource &capture_source = *parse_userdata->capture_source;
+ if(size == 0)
+ return true;
- // First column contains the capture target
- if(is_first_column) {
- is_first_column = false;
- return true;
- }
+ if(parse_userdata->is_first_column) {
+ parse_userdata->is_first_column = false;
+ return true;
+ }
- if(string_starts_with(sub, size, "x=")) {
- capture_source.pos.x_type = sub[size - 1] == '%' ? VVEC2I_TYPE_SCALAR : VVEC2I_TYPE_PIXELS;
- sub += 2;
- size -= 2;
- if(!string_to_int(sub, size, &capture_source.pos.x)) {
- gsr_log(GSR_LOG_LEVEL_ERROR, "invalid capture target value for option x: \"%.*s\", expected a number", (int)size, sub);
- _exit(1);
- }
- } else if(string_starts_with(sub, size, "y=")) {
- capture_source.pos.y_type = sub[size - 1] == '%' ? VVEC2I_TYPE_SCALAR : VVEC2I_TYPE_PIXELS;
- sub += 2;
- size -= 2;
- if(!string_to_int(sub, size, &capture_source.pos.y)) {
- gsr_log(GSR_LOG_LEVEL_ERROR, "invalid capture target value for option y: \"%.*s\", expected a number", (int)size, sub);
- _exit(1);
- }
+ if(gsr_string_starts_with(sub, size, "x=")) {
+ capture_source.pos.x_type = sub[size - 1] == '%' ? VVEC2I_TYPE_SCALAR : VVEC2I_TYPE_PIXELS;
+ sub += 2;
+ size -= 2;
+ if(!gsr_string_to_int(sub, size, &capture_source.pos.x)) {
+ gsr_log(GSR_LOG_LEVEL_ERROR, "invalid capture target value for option x: \"%.*s\", expected a number", (int)size, sub);
+ _exit(1);
+ }
+ } else if(gsr_string_starts_with(sub, size, "y=")) {
+ capture_source.pos.y_type = sub[size - 1] == '%' ? VVEC2I_TYPE_SCALAR : VVEC2I_TYPE_PIXELS;
+ sub += 2;
+ size -= 2;
+ if(!gsr_string_to_int(sub, size, &capture_source.pos.y)) {
+ gsr_log(GSR_LOG_LEVEL_ERROR, "invalid capture target value for option y: \"%.*s\", expected a number", (int)size, sub);
+ _exit(1);
+ }
- } else if(string_starts_with(sub, size, "width=")) {
- capture_source.size.x_type = sub[size - 1] == '%' ? VVEC2I_TYPE_SCALAR : VVEC2I_TYPE_PIXELS;
- sub += 6;
- size -= 6;
- if(!string_to_int(sub, size, &capture_source.size.x)) {
- gsr_log(GSR_LOG_LEVEL_ERROR, "invalid capture target value for option width: \"%.*s\", expected a number", (int)size, sub);
- _exit(1);
- }
- } else if(string_starts_with(sub, size, "height=")) {
- capture_source.size.y_type = sub[size - 1] == '%' ? VVEC2I_TYPE_SCALAR : VVEC2I_TYPE_PIXELS;
- sub += 7;
- size -= 7;
- if(!string_to_int(sub, size, &capture_source.size.y)) {
- gsr_log(GSR_LOG_LEVEL_ERROR, "invalid capture target value for option height: \"%.*s\", expected a number", (int)size, sub);
- _exit(1);
- }
- } else if(string_starts_with(sub, size, "halign=")) {
- sub += 7;
- size -= 7;
- if(!string_to_capture_alignment(sub, size, &capture_source.halign)) {
- gsr_log(GSR_LOG_LEVEL_ERROR, "invalid capture target value for option halign: \"%.*s\", expected a \"start\", \"center\" or \"end\"", (int)size, sub);
- _exit(1);
- }
- } else if(string_starts_with(sub, size, "valign=")) {
- sub += 7;
- size -= 7;
- if(!string_to_capture_alignment(sub, size, &capture_source.valign)) {
- gsr_log(GSR_LOG_LEVEL_ERROR, "invalid capture target value for option valign: \"%.*s\", expected a \"start\", \"center\" or \"end\"", (int)size, sub);
- _exit(1);
- }
- } else if(string_starts_with(sub, size, "pixfmt=")) {
- sub += 7;
- size -= 7;
- if(!string_to_v4l2_pixfmt(sub, size, &capture_source.v4l2_pixfmt)) {
- gsr_log(GSR_LOG_LEVEL_ERROR, "invalid v4l2 pixfmt value for option pixfmt: \"%.*s\", expected a \"auto\", \"yuyv\" or \"mjpeg\"", (int)size, sub);
- _exit(1);
- }
- } else if(string_starts_with(sub, size, "hflip=")) {
- sub += 6;
- size -= 6;
- bool hflip = false;
- if(!string_to_bool(sub, size, &hflip)) {
- gsr_log(GSR_LOG_LEVEL_ERROR, "invalid bool value for option hflip: \"%.*s\", expected a \"true\" or \"false\"", (int)size, sub);
- _exit(1);
- }
+ } else if(gsr_string_starts_with(sub, size, "width=")) {
+ capture_source.size.x_type = sub[size - 1] == '%' ? VVEC2I_TYPE_SCALAR : VVEC2I_TYPE_PIXELS;
+ sub += 6;
+ size -= 6;
+ if(!gsr_string_to_int(sub, size, &capture_source.size.x)) {
+ gsr_log(GSR_LOG_LEVEL_ERROR, "invalid capture target value for option width: \"%.*s\", expected a number", (int)size, sub);
+ _exit(1);
+ }
+ } else if(gsr_string_starts_with(sub, size, "height=")) {
+ capture_source.size.y_type = sub[size - 1] == '%' ? VVEC2I_TYPE_SCALAR : VVEC2I_TYPE_PIXELS;
+ sub += 7;
+ size -= 7;
+ if(!gsr_string_to_int(sub, size, &capture_source.size.y)) {
+ gsr_log(GSR_LOG_LEVEL_ERROR, "invalid capture target value for option height: \"%.*s\", expected a number", (int)size, sub);
+ _exit(1);
+ }
+ } else if(gsr_string_starts_with(sub, size, "halign=")) {
+ sub += 7;
+ size -= 7;
+ if(!string_to_capture_alignment(sub, size, &capture_source.halign)) {
+ gsr_log(GSR_LOG_LEVEL_ERROR, "invalid capture target value for option halign: \"%.*s\", expected a \"start\", \"center\" or \"end\"", (int)size, sub);
+ _exit(1);
+ }
+ } else if(gsr_string_starts_with(sub, size, "valign=")) {
+ sub += 7;
+ size -= 7;
+ if(!string_to_capture_alignment(sub, size, &capture_source.valign)) {
+ gsr_log(GSR_LOG_LEVEL_ERROR, "invalid capture target value for option valign: \"%.*s\", expected a \"start\", \"center\" or \"end\"", (int)size, sub);
+ _exit(1);
+ }
+ } else if(gsr_string_starts_with(sub, size, "pixfmt=")) {
+ sub += 7;
+ size -= 7;
+ if(!string_to_v4l2_pixfmt(sub, size, &capture_source.v4l2_pixfmt)) {
+ gsr_log(GSR_LOG_LEVEL_ERROR, "invalid v4l2 pixfmt value for option pixfmt: \"%.*s\", expected a \"auto\", \"yuyv\" or \"mjpeg\"", (int)size, sub);
+ _exit(1);
+ }
+ } else if(gsr_string_starts_with(sub, size, "hflip=")) {
+ sub += 6;
+ size -= 6;
+ bool hflip = false;
+ if(!string_to_bool(sub, size, &hflip)) {
+ gsr_log(GSR_LOG_LEVEL_ERROR, "invalid bool value for option hflip: \"%.*s\", expected a \"true\" or \"false\"", (int)size, sub);
+ _exit(1);
+ }
- if(hflip)
- capture_source.flip |= GSR_FLIP_HORIZONTAL;
- } else if(string_starts_with(sub, size, "vflip=")) {
- sub += 6;
- size -= 6;
- bool vflip = false;
- if(!string_to_bool(sub, size, &vflip)) {
- gsr_log(GSR_LOG_LEVEL_ERROR, "invalid bool value for option vflip: \"%.*s\", expected a \"true\" or \"false\"", (int)size, sub);
- _exit(1);
- }
+ if(hflip)
+ capture_source.flip |= GSR_FLIP_HORIZONTAL;
+ } else if(gsr_string_starts_with(sub, size, "vflip=")) {
+ sub += 6;
+ size -= 6;
+ bool vflip = false;
+ if(!string_to_bool(sub, size, &vflip)) {
+ gsr_log(GSR_LOG_LEVEL_ERROR, "invalid bool value for option vflip: \"%.*s\", expected a \"true\" or \"false\"", (int)size, sub);
+ _exit(1);
+ }
- if(vflip)
- capture_source.flip |= GSR_FLIP_VERTICAL;
- } else if(string_starts_with(sub, size, "camera_fps=")) {
- sub += 11;
- size -= 11;
- if(!string_to_int(sub, size, &capture_source.camera_fps)) {
- gsr_log(GSR_LOG_LEVEL_ERROR, "invalid capture target value for option camera_fps: \"%.*s\", expected a number", (int)size, sub);
- _exit(1);
- }
- } else if(string_starts_with(sub, size, "camera_width=")) {
- sub += 13;
- size -= 13;
- if(!string_to_int(sub, size, &capture_source.camera_resolution.x)) {
- gsr_log(GSR_LOG_LEVEL_ERROR, "invalid capture target value for option camera_width: \"%.*s\", expected a number", (int)size, sub);
- _exit(1);
- }
- } else if(string_starts_with(sub, size, "camera_height=")) {
- sub += 14;
- size -= 14;
- if(!string_to_int(sub, size, &capture_source.camera_resolution.y)) {
- gsr_log(GSR_LOG_LEVEL_ERROR, "invalid capture target value for option camera_height: \"%.*s\", expected a number", (int)size, sub);
- _exit(1);
- }
- } else {
- gsr_log(GSR_LOG_LEVEL_ERROR, "invalid capture target option \"%.*s\", expected x, y, width, height, halign, valign, pixfmt, hflip, vflip, camera_fps, camera_width or camera_height", (int)size, sub);
- _exit(1);
+ if(vflip)
+ capture_source.flip |= GSR_FLIP_VERTICAL;
+ } else if(gsr_string_starts_with(sub, size, "camera_fps=")) {
+ sub += 11;
+ size -= 11;
+ if(!gsr_string_to_int(sub, size, &capture_source.camera_fps)) {
+ gsr_log(GSR_LOG_LEVEL_ERROR, "invalid capture target value for option camera_fps: \"%.*s\", expected a number", (int)size, sub);
+ _exit(1);
+ }
+ } else if(gsr_string_starts_with(sub, size, "camera_width=")) {
+ sub += 13;
+ size -= 13;
+ if(!gsr_string_to_int(sub, size, &capture_source.camera_resolution.x)) {
+ gsr_log(GSR_LOG_LEVEL_ERROR, "invalid capture target value for option camera_width: \"%.*s\", expected a number", (int)size, sub);
+ _exit(1);
}
+ } else if(gsr_string_starts_with(sub, size, "camera_height=")) {
+ sub += 14;
+ size -= 14;
+ if(!gsr_string_to_int(sub, size, &capture_source.camera_resolution.y)) {
+ gsr_log(GSR_LOG_LEVEL_ERROR, "invalid capture target value for option camera_height: \"%.*s\", expected a number", (int)size, sub);
+ _exit(1);
+ }
+ } else {
+ gsr_log(GSR_LOG_LEVEL_ERROR, "invalid capture target option \"%.*s\", expected x, y, width, height, halign, valign, pixfmt, hflip, vflip, camera_fps, camera_width or camera_height", (int)size, sub);
+ _exit(1);
+ }
- return true;
- });
+ return true;
}
-static std::vector<CaptureSource> parse_capture_source_arg(const char *capture_source_arg, const args_parser &arg_parser) {
- std::vector<CaptureSource> requested_capture_sources;
- const bool has_multiple_capture_sources = strchr(capture_source_arg, '|') != nullptr;
+static void parse_capture_source_options(const std::string &capture_source_str, CaptureSource &capture_source) {
+ ParseCaptureSourceOptionsUserdata userdata;
+ userdata.capture_source = &capture_source;
+ userdata.is_first_column = true;
+ gsr_string_split(capture_source_str.c_str(), ';', parse_capture_source_options_callback, &userdata);
+}
- split_string(capture_source_arg, '|', [&](const char *sub, size_t size) {
- if(size == 0)
- return true;
+struct ParseCaptureSourceArgUserdata {
+ std::vector<CaptureSource> *requested_capture_sources;
+ const args_parser *arg_parser;
+ bool has_multiple_capture_sources;
+};
- const char *substr_start = sub;
- size_t capture_source_size = size;
- const char *capture_source_end = (const char*)memchr(sub, ';', size);
- if(capture_source_end)
- capture_source_size = capture_source_end - sub;
-
- CaptureSource capture_source;
- capture_source.region_pos = arg_parser.region_position;
- capture_source.region_size = arg_parser.region_size;
-
- if(string_starts_with(sub, capture_source_size, "monitor:")) {
- capture_source.type = GSR_CAPTURE_SOURCE_TYPE_MONITOR;
- sub += 8;
- capture_source_size -= 8;
- } else if(string_starts_with(sub, capture_source_size, "window:")) {
- capture_source.type = GSR_CAPTURE_SOURCE_TYPE_WINDOW;
- sub += 7;
- capture_source_size -= 7;
- } else if(string_starts_with(sub, capture_source_size, "v4l2:")) {
- capture_source.type = GSR_CAPTURE_SOURCE_TYPE_V4L2;
- sub += 5;
- capture_source_size -= 5;
- } else {
- capture_source_type_from_string(sub, capture_source_size, capture_source);
- }
+static bool parse_capture_source_arg_callback(const char *sub, size_t size, void *userdata) {
+ ParseCaptureSourceArgUserdata *parse_userdata = (ParseCaptureSourceArgUserdata*)userdata;
+ if(size == 0)
+ return true;
- capture_source.name.assign(sub, capture_source_size);
+ const char *substr_start = sub;
+ size_t capture_source_size = size;
+ const char *capture_source_end = (const char*)memchr(sub, ';', size);
+ if(capture_source_end)
+ capture_source_size = capture_source_end - sub;
- if(capture_source.type == GSR_CAPTURE_SOURCE_TYPE_WINDOW) {
- if(!string_to_int(capture_source.name.c_str(), capture_source.name.size(), &capture_source.window_id)) {
- gsr_log(GSR_LOG_LEVEL_ERROR, "invalid window number %s", capture_source.name.c_str());
- args_parser_print_usage();
- _exit(1);
- }
- }
+ CaptureSource capture_source;
+ capture_source.region_pos = parse_userdata->arg_parser->region_position;
+ capture_source.region_size = parse_userdata->arg_parser->region_size;
+
+ if(gsr_string_starts_with(sub, capture_source_size, "monitor:")) {
+ capture_source.type = GSR_CAPTURE_SOURCE_TYPE_MONITOR;
+ sub += 8;
+ capture_source_size -= 8;
+ } else if(gsr_string_starts_with(sub, capture_source_size, "window:")) {
+ capture_source.type = GSR_CAPTURE_SOURCE_TYPE_WINDOW;
+ sub += 7;
+ capture_source_size -= 7;
+ } else if(gsr_string_starts_with(sub, capture_source_size, "v4l2:")) {
+ capture_source.type = GSR_CAPTURE_SOURCE_TYPE_V4L2;
+ sub += 5;
+ capture_source_size -= 5;
+ } else {
+ capture_source_type_from_string(sub, capture_source_size, capture_source);
+ }
- if(has_multiple_capture_sources) {
- capture_source.halign = GSR_CAPTURE_ALIGN_START;
- capture_source.valign = GSR_CAPTURE_ALIGN_START;
- capture_source.pos = {0, 0, VVEC2I_TYPE_PIXELS, VVEC2I_TYPE_PIXELS};
+ capture_source.name.assign(sub, capture_source_size);
+
+ if(capture_source.type == GSR_CAPTURE_SOURCE_TYPE_WINDOW) {
+ if(!gsr_string_to_int64(capture_source.name.c_str(), capture_source.name.size(), &capture_source.window_id)) {
+ gsr_log(GSR_LOG_LEVEL_ERROR, "invalid window number %s", capture_source.name.c_str());
+ args_parser_print_usage();
+ _exit(1);
}
+ }
- parse_capture_source_options(std::string(substr_start, size), capture_source);
- requested_capture_sources.push_back(capture_source);
- return true;
- });
+ if(parse_userdata->has_multiple_capture_sources) {
+ capture_source.halign = GSR_CAPTURE_ALIGN_START;
+ capture_source.valign = GSR_CAPTURE_ALIGN_START;
+ capture_source.pos = {0, 0, VVEC2I_TYPE_PIXELS, VVEC2I_TYPE_PIXELS};
+ }
+
+ parse_capture_source_options(std::string(substr_start, size), capture_source);
+ parse_userdata->requested_capture_sources->push_back(capture_source);
+ return true;
+}
+static std::vector<CaptureSource> parse_capture_source_arg(const char *capture_source_arg, const args_parser &arg_parser) {
+ std::vector<CaptureSource> requested_capture_sources;
+ ParseCaptureSourceArgUserdata userdata;
+ userdata.requested_capture_sources = &requested_capture_sources;
+ userdata.arg_parser = &arg_parser;
+ userdata.has_multiple_capture_sources = strchr(capture_source_arg, '|') != nullptr;
+ gsr_string_split(capture_source_arg, '|', parse_capture_source_arg_callback, &userdata);
return requested_capture_sources;
}
@@ -3462,10 +3420,10 @@ static AudioDeviceData create_application_audio_audio_input(const MergedAudioInp
#endif
static bool get_image_format_from_filename(const char *filename, gsr_image_format *image_format) {
- if(string_ends_with(filename, ".jpg") || string_ends_with(filename, ".jpeg")) {
+ if(gsr_string_ends_with(filename, ".jpg") || gsr_string_ends_with(filename, ".jpeg")) {
*image_format = GSR_IMAGE_FORMAT_JPEG;
return true;
- } else if(string_ends_with(filename, ".png")) {
+ } else if(gsr_string_ends_with(filename, ".png")) {
*image_format = GSR_IMAGE_FORMAT_PNG;
return true;
} else {
diff --git a/src/pipewire_audio.c b/src/pipewire_audio.c
index be1ba21..73192a9 100644
--- a/src/pipewire_audio.c
+++ b/src/pipewire_audio.c
@@ -1,5 +1,6 @@
#include "../include/pipewire_audio.h"
#include "../include/log.h"
+#include "../include/utils.h"
#include <pipewire/pipewire.h>
#include <pipewire/extensions/metadata.h>
@@ -400,24 +401,6 @@ 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) {
- gsr_log(GSR_LOG_LEVEL_ERROR, "pipewire_audio: failed to reallocate memory");
- return false;
- }
-
- *array = new_data;
- *capacity_items = new_capacity_items;
- }
- return true;
-}
-
struct gsr_pipewire_audio_node_binding {
gsr_pipewire_audio *self;
struct pw_proxy *proxy;
@@ -435,7 +418,7 @@ static gsr_pipewire_audio_node* gsr_pipewire_audio_get_node_by_id(gsr_pipewire_a
}
static void gsr_pipewire_audio_add_node(gsr_pipewire_audio *self, uint32_t id, const char *node_name, gsr_pipewire_audio_node_type type, bool is_virtual, bool is_application) {
- if(!array_ensure_capacity((void**)&self->stream_nodes, self->num_stream_nodes, &self->stream_nodes_capacity_items, sizeof(gsr_pipewire_audio_node)))
+ if(!gsr_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);
@@ -525,7 +508,7 @@ static const struct pw_proxy_events node_proxy_events = {
/* The registry only broadcasts a small set of the node properties (node.name, media.class, application.name, etc).
Binding to the node is required to get the other properties (node.virtual, application.id, etc) from the node info event. */
static void gsr_pipewire_audio_bind_node(gsr_pipewire_audio *self, uint32_t id) {
- if(!array_ensure_capacity((void**)&self->node_bindings, self->num_node_bindings, &self->node_bindings_capacity_items, sizeof(gsr_pipewire_audio_node_binding*)))
+ if(!gsr_array_ensure_capacity((void**)&self->node_bindings, self->num_node_bindings, &self->node_bindings_capacity_items, sizeof(gsr_pipewire_audio_node_binding*)))
return;
gsr_pipewire_audio_node_binding *node_binding = calloc(1, sizeof(gsr_pipewire_audio_node_binding));
@@ -592,7 +575,7 @@ static void registry_event_global(void *data, uint32_t id, uint32_t permissions,
const int node_id_num = node_id ? atoi(node_id) : 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)))
+ if(!gsr_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);
@@ -615,7 +598,7 @@ 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(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)))
+ if(!gsr_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);
@@ -891,7 +874,7 @@ 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(!array_ensure_capacity((void**)&self->requested_links, self->num_requested_links, &self->requested_links_capacity_items, sizeof(gsr_pipewire_audio_requested_link)))
+ if(!gsr_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));
diff --git a/src/utils.c b/src/utils.c
index 75a6427..9ae34d7 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -683,3 +683,89 @@ bool get_nvidia_driver_version(int *major, int *minor) {
fclose(f);
return success;
}
+
+void gsr_string_split(const char *str, char delimiter, gsr_string_split_callback callback, void *userdata) {
+ const size_t str_len = strlen(str);
+ size_t index = 0;
+ while(index < str_len) {
+ const char *end = strchr(str + index, delimiter);
+ const size_t end_index = end ? (size_t)(end - str) : str_len;
+
+ if(!callback(str + index, end_index - index, userdata))
+ break;
+
+ index = end_index + 1;
+ }
+}
+
+bool gsr_string_starts_with(const char *str, size_t str_size, const char *substr) {
+ const size_t substr_len = strlen(substr);
+ return str_size >= substr_len && memcmp(str, substr, substr_len) == 0;
+}
+
+bool gsr_string_ends_with(const char *str, const char *substr) {
+ const size_t str_len = strlen(str);
+ const size_t substr_len = strlen(substr);
+ return str_len >= substr_len && memcmp(str + str_len - substr_len, substr, substr_len) == 0;
+}
+
+static bool string_to_long(const char *str, size_t size, long *number) {
+ char number_str[32];
+ snprintf(number_str, sizeof(number_str), "%.*s", (int)size, str);
+
+ errno = 0;
+ *number = strtol(number_str, NULL, 0);
+ return errno == 0;
+}
+
+bool gsr_string_to_int(const char *str, size_t size, int *number) {
+ long value = 0;
+ if(!string_to_long(str, size, &value))
+ return false;
+ *number = value;
+ return true;
+}
+
+bool gsr_string_to_int64(const char *str, size_t size, int64_t *number) {
+ long value = 0;
+ if(!string_to_long(str, size, &value))
+ return false;
+ *number = value;
+ return true;
+}
+
+bool gsr_array_ensure_capacity(void **array, size_t num_items, size_t *capacity_items, size_t item_size) {
+ if(num_items + 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 * item_size);
+ if(!new_data) {
+ gsr_log(GSR_LOG_LEVEL_ERROR, "gsr_array_ensure_capacity: failed to reallocate memory");
+ return false;
+ }
+
+ *array = new_data;
+ *capacity_items = new_capacity_items;
+ }
+ return true;
+}
+
+void gsr_get_date_str(char *str, size_t size) {
+ time_t now = time(NULL);
+ struct tm *t = localtime(&now);
+ strftime(str, size - 1, "%Y-%m-%d_%H-%M-%S", t);
+}
+
+void gsr_get_date_only_str(char *str, size_t size) {
+ time_t now = time(NULL);
+ struct tm *t = localtime(&now);
+ strftime(str, size - 1, "%Y-%m-%d", t);
+}
+
+void gsr_get_time_only_str(char *str, size_t size) {
+ time_t now = time(NULL);
+ struct tm *t = localtime(&now);
+ strftime(str, size - 1, "%H-%M-%S", t);
+}