From f7606a144b7147e493304278d321ab2ed7996bb8 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Fri, 30 Sep 2022 22:02:23 +0200 Subject: [PATCH] Give error when using an invalid audio input with pipewire --- README.md | 2 +- include/sound.hpp | 10 +++++++ src/main.cpp | 22 ++++++++++++++ src/sound.cpp | 73 ++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 105 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9cd1ac3..5c3df22 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Run `scripts/interactive.sh` or run gpu-screen-recorder directly, for example: ` Then stop the screen recorder with Ctrl+C, which will also save the recording.\ Send signal SIGUSR1 (`killall -SIGUSR1 gpu-screen-recorder`) to gpu-screen-recorder when in replay mode to save the replay. The paths to the saved files is output to stdout after the recording is saved.\ You can find the default output audio device (headset, speakers) with the command `pactl get-default-sink`. Add `monitor` to the end of that to use that as an audio input in gpu-screen-recorder.\ -You can find the default input audio device (microphone) with the command `pactl get-default-source`.\ +You can find the default input audio device (microphone) with the command `pactl get-default-source`. This input should not have `monitor` added to the end when used in gpu-screen-recorder.\ There is also a gui for the gpu-screen-recorder called [gpu-screen-recorder-gtk](https://git.dec05eba.com/gpu-screen-recorder-gtk/). # Demo diff --git a/include/sound.hpp b/include/sound.hpp index 39d9248..d8ad322 100644 --- a/include/sound.hpp +++ b/include/sound.hpp @@ -18,11 +18,19 @@ #ifndef GPU_SCREEN_RECORDER_H #define GPU_SCREEN_RECORDER_H +#include +#include + typedef struct { void *handle; unsigned int frames; } SoundDevice; +struct AudioInput { + std::string name; + std::string description; +}; + /* Get a sound device by name, returning the device into the @device parameter. The device should be closed with @sound_device_close after it has been used @@ -39,4 +47,6 @@ void sound_device_close(SoundDevice *device); */ int sound_device_read_next_chunk(SoundDevice *device, void **buffer); +std::vector get_pulseaudio_inputs(); + #endif /* GPU_SCREEN_RECORDER_H */ diff --git a/src/main.cpp b/src/main.cpp index caea9b6..61eb5cf 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1073,6 +1073,28 @@ int main(int argc, char **argv) { } Arg &audio_input_arg = args["-a"]; + const std::vector audio_inputs = get_pulseaudio_inputs(); + + // Manually check if the audio inputs we give exist. This is only needed for pipewire, not pulseaudio. + // Pipewire instead DEFAULTS TO THE DEFAULT AUDIO INPUT. THAT'S RETARDED. + // OH, YOU MISSPELLED THE AUDIO INPUT? FUCK YOU + for(const char *audio_input : audio_input_arg.values) { + bool match = false; + for(const auto &existing_audio_input : audio_inputs) { + if(strcmp(audio_input, existing_audio_input.name.c_str()) == 0) { + match = true; + break; + } + } + + if(!match) { + fprintf(stderr, "Error: Audio input device '%s' is not a valid input device. Expected one of:\n", audio_input); + for(const auto &existing_audio_input : audio_inputs) { + fprintf(stderr, " %s\n", existing_audio_input.name.c_str()); + } + exit(2); + } + } uint32_t region_x = 0; uint32_t region_y = 0; diff --git a/src/sound.cpp b/src/sound.cpp index abd2e65..0fd1333 100644 --- a/src/sound.cpp +++ b/src/sound.cpp @@ -277,7 +277,7 @@ int sound_device_get_by_name(SoundDevice *device, const char *name, unsigned int pa_handle *handle = pa_sound_device_new(nullptr, stream_name, name, stream_name, &ss, &buffer_attr, &error); if(!handle) { - fprintf(stderr, "pa_simple_new() failed: %s. Audio input device %s might not be valid\n", pa_strerror(error), name); + fprintf(stderr, "pa_sound_device_new() failed: %s. Audio input device %s might not be valid\n", pa_strerror(error), name); return -1; } @@ -298,4 +298,75 @@ int sound_device_read_next_chunk(SoundDevice *device, void **buffer) { } *buffer = pa->output_data; return device->frames; +} + +static void pa_state_cb(pa_context *c, void *userdata) { + pa_context_state state = pa_context_get_state(c); + int *pa_ready = (int*)userdata; + switch(state) { + case PA_CONTEXT_UNCONNECTED: + case PA_CONTEXT_CONNECTING: + case PA_CONTEXT_AUTHORIZING: + case PA_CONTEXT_SETTING_NAME: + default: + break; + case PA_CONTEXT_FAILED: + case PA_CONTEXT_TERMINATED: + *pa_ready = 2; + break; + case PA_CONTEXT_READY: + *pa_ready = 1; + break; + } +} + +static void pa_sourcelist_cb(pa_context *ctx, const pa_source_info *source_info, int eol, void *userdata) { + if(eol > 0) + return; + + std::vector *inputs = (std::vector*)userdata; + inputs->push_back({ source_info->name, source_info->description }); +} + +std::vector get_pulseaudio_inputs() { + std::vector inputs; + pa_mainloop *main_loop = pa_mainloop_new(); + + pa_context *ctx = pa_context_new(pa_mainloop_get_api(main_loop), "gpu-screen-recorder"); + pa_context_connect(ctx, NULL, PA_CONTEXT_NOFLAGS, NULL); + int state = 0; + int pa_ready = 0; + pa_context_set_state_callback(ctx, pa_state_cb, &pa_ready); + + pa_operation *pa_op = NULL; + + for(;;) { + // Not ready + if(pa_ready == 0) { + pa_mainloop_iterate(main_loop, 1, NULL); + continue; + } + + switch(state) { + case 0: { + pa_op = pa_context_get_source_info_list(ctx, pa_sourcelist_cb, &inputs); + ++state; + break; + } + } + + // Couldn't get connection to the server + if(pa_ready == 2 || (state == 1 && pa_op && pa_operation_get_state(pa_op) == PA_OPERATION_DONE)) { + if(pa_op) + pa_operation_unref(pa_op); + pa_context_disconnect(ctx); + pa_context_unref(ctx); + pa_mainloop_free(main_loop); + return inputs; + } + + pa_mainloop_iterate(main_loop, 1, NULL); + } + + pa_mainloop_free(main_loop); } \ No newline at end of file