diff options
author | dec05eba <dec05eba@protonmail.com> | 2024-08-02 00:12:39 +0200 |
---|---|---|
committer | dec05eba <dec05eba@protonmail.com> | 2024-08-02 00:14:01 +0200 |
commit | 844a54b43b3330711e5f3c5e4680f960538f8eb5 (patch) | |
tree | 32275e752fdd496ac660376a817a7c2cd7c57d14 | |
parent | 7e1be0192d39421daa2b29fbcb1838889704dd8a (diff) |
Allow using 'default_output' / 'default_input' as audio devices (-a option), add --list-audio-devices option
-rw-r--r-- | include/sound.hpp | 8 | ||||
-rw-r--r-- | src/main.cpp | 129 | ||||
-rw-r--r-- | src/sound.cpp | 66 |
3 files changed, 160 insertions, 43 deletions
diff --git a/include/sound.hpp b/include/sound.hpp index 77bec99..7bcc120 100644 --- a/include/sound.hpp +++ b/include/sound.hpp @@ -31,6 +31,12 @@ struct AudioInput { std::string description; }; +struct AudioDevices { + std::string default_output; + std::string default_input; + std::vector<AudioInput> audio_inputs; +}; + struct MergedAudioInputs { std::vector<AudioInput> audio_inputs; }; @@ -57,6 +63,6 @@ void sound_device_close(SoundDevice *device); */ int sound_device_read_next_chunk(SoundDevice *device, void **buffer, double timeout_sec, double *latency_seconds); -std::vector<AudioInput> get_pulseaudio_inputs(); +AudioDevices get_pulseaudio_inputs(); #endif /* GPU_SCREEN_RECORDER_H */ diff --git a/src/main.cpp b/src/main.cpp index 50fa7f8..48c6223 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1036,6 +1036,7 @@ static void usage_full() { fprintf(stderr, " -a Audio device to record from (pulse audio device). Can be specified multiple times. Each time this is specified a new audio track is added for the specified audio device.\n"); fprintf(stderr, " A name can be given to the audio input device by prefixing the audio input with <name>/, for example \"dummy/alsa_output.pci-0000_00_1b.0.analog-stereo.monitor\".\n"); fprintf(stderr, " Multiple audio devices can be merged into one audio track by using \"|\" as a separator into one -a argument, for example: -a \"alsa_output1|alsa_output2\".\n"); + fprintf(stderr, " The audio device can also be \"default_output\" in which case the default output device is used, or \"default_input\" in which case the default input device is used.\n"); fprintf(stderr, " If the audio device is an empty string then the audio device is ignored.\n"); fprintf(stderr, " Optional, no audio track is added by default.\n"); fprintf(stderr, "\n"); @@ -1097,6 +1098,13 @@ static void usage_full() { fprintf(stderr, " Supported capture options (window, focused, screen, monitors and portal, if supported by the system).\n"); fprintf(stderr, " If opengl initialization fails then the program exits with 22, if no usable drm device is found then it exits with 23. On success it exits with 0.\n"); fprintf(stderr, "\n"); + fprintf(stderr, " --list-audio-devices\n"); + fprintf(stderr, " List audio devices (for use by GPU Screen Recorder UI). Lists audio devices in the following format (prints them to stdout and exits):\n"); + fprintf(stderr, " <audio_device_name> <audio_device_name_in_human_readable_format>\n"); + fprintf(stderr, " For example:\n"); + fprintf(stderr, " bluez_input.88:C9:E8:66:A2:27 WH-1000XM4\n"); + fprintf(stderr, " The <audio_device_name> is the name to pass to GPU Screen Recorder in a -a option.\n"); + fprintf(stderr, "\n"); //fprintf(stderr, " -pixfmt The pixel format to use for the output video. yuv420 is the most common format and is best supported, but the color is compressed, so colors can look washed out and certain colors of text can look bad. Use yuv444 for no color compression, but the video may not work everywhere and it may not work with hardware video decoding. Optional, set to 'yuv420' by default\n"); fprintf(stderr, " -o The output file path. If omitted then the encoded data is sent to stdout. Required in replay mode (when using -r).\n"); fprintf(stderr, " In replay mode this has to be a directory instead of a file.\n"); @@ -1782,6 +1790,23 @@ static void info_command() { _exit(0); } +static void list_audio_devices_command() { + const AudioDevices audio_devices = get_pulseaudio_inputs(); + + if(!audio_devices.default_output.empty()) + puts("default_output Default output"); + + if(!audio_devices.default_input.empty()) + puts("default_input Default input"); + + for(const auto &audio_input : audio_devices.audio_inputs) { + printf("%s %s\n", audio_input.name.c_str(), audio_input.description.c_str()); + } + + fflush(stdout); + _exit(0); +} + static gsr_capture* create_capture_impl(const char *window_str, const char *screen_region, bool wayland, gsr_egl *egl, int fps, bool overclock, VideoCodec video_codec, gsr_color_range color_range, bool record_cursor, bool track_damage, bool use_software_video_encoder, bool restore_portal_session) { vec2i region_size = { 0, 0 }; Window src_window_id = None; @@ -1998,6 +2023,65 @@ struct Arg { } }; +// 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 +static std::vector<MergedAudioInputs> parse_audio_inputs(const AudioDevices &audio_devices, const Arg &audio_input_arg, bool &uses_amix) { + std::vector<MergedAudioInputs> requested_audio_inputs; + uses_amix = false; + + for(const char *audio_input : audio_input_arg.values) { + if(!audio_input || audio_input[0] == '\0') + continue; + + requested_audio_inputs.push_back({parse_audio_input_arg(audio_input)}); + if(requested_audio_inputs.back().audio_inputs.size() > 1) + uses_amix = true; + + for(AudioInput &request_audio_input : requested_audio_inputs.back().audio_inputs) { + bool match = false; + + if(!audio_devices.default_output.empty() && request_audio_input.name == "default_output") { + request_audio_input.name = audio_devices.default_output; + if(request_audio_input.description.empty()) + request_audio_input.description = "gsr-Default output"; + match = true; + } + + if(!audio_devices.default_input.empty() && request_audio_input.name == "default_input") { + request_audio_input.name = audio_devices.default_input; + if(request_audio_input.description.empty()) + request_audio_input.description = "gsr-Default input"; + match = true; + } + + for(const auto &existing_audio_input : audio_devices.audio_inputs) { + if(request_audio_input.name == existing_audio_input.name) { + if(request_audio_input.description.empty()) + request_audio_input.description = "gsr-" + existing_audio_input.description; + + match = true; + break; + } + } + + if(!match) { + fprintf(stderr, "Error: Audio input 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()) + fprintf(stderr, " default_input (Default input)\n"); + for(const auto &existing_audio_input : audio_devices.audio_inputs) { + fprintf(stderr, " %s (%s)\n", existing_audio_input.name.c_str(), existing_audio_input.description.c_str()); + } + _exit(2); + } + } + } + + return requested_audio_inputs; +} + int main(int argc, char **argv) { signal(SIGINT, stop_handler); signal(SIGUSR1, save_replay_handler); @@ -2033,6 +2117,11 @@ int main(int argc, char **argv) { _exit(0); } + if(argc == 2 && strcmp(argv[1], "--list-audio-devices") == 0) { + list_audio_devices_command(); + _exit(0); + } + //av_log_set_level(AV_LOG_TRACE); std::map<std::string, Arg> args = { @@ -2274,44 +2363,12 @@ int main(int argc, char **argv) { } const Arg &audio_input_arg = args["-a"]; - std::vector<AudioInput> audio_inputs; + AudioDevices audio_devices; if(!audio_input_arg.values.empty()) - audio_inputs = get_pulseaudio_inputs(); - std::vector<MergedAudioInputs> requested_audio_inputs; - bool uses_amix = false; - - // 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) { - if(!audio_input || audio_input[0] == '\0') - continue; - - requested_audio_inputs.push_back({parse_audio_input_arg(audio_input)}); - if(requested_audio_inputs.back().audio_inputs.size() > 1) - uses_amix = true; + audio_devices = get_pulseaudio_inputs(); - for(AudioInput &request_audio_input : requested_audio_inputs.back().audio_inputs) { - bool match = false; - for(const auto &existing_audio_input : audio_inputs) { - if(strcmp(request_audio_input.name.c_str(), existing_audio_input.name.c_str()) == 0) { - if(request_audio_input.description.empty()) - request_audio_input.description = "gsr-" + existing_audio_input.description; - - match = true; - break; - } - } - - if(!match) { - fprintf(stderr, "Error: Audio input device '%s' is not a valid audio device, expected one of:\n", request_audio_input.name.c_str()); - for(const auto &existing_audio_input : audio_inputs) { - fprintf(stderr, " %s (%s)\n", existing_audio_input.name.c_str(), existing_audio_input.description.c_str()); - } - _exit(2); - } - } - } + bool uses_amix = false; + std::vector<MergedAudioInputs> requested_audio_inputs = parse_audio_inputs(audio_devices, audio_input_arg, uses_amix); const char *container_format = args["-c"].value(); if(container_format && strcmp(container_format, "mkv") == 0) diff --git a/src/sound.cpp b/src/sound.cpp index 53000bd..d0f2a80 100644 --- a/src/sound.cpp +++ b/src/sound.cpp @@ -327,12 +327,66 @@ static void pa_sourcelist_cb(pa_context *ctx, const pa_source_info *source_info, if(eol > 0) return; - std::vector<AudioInput> *inputs = (std::vector<AudioInput>*)userdata; - inputs->push_back({ source_info->name, source_info->description }); + AudioDevices *audio_devices = (AudioDevices*)userdata; + audio_devices->audio_inputs.push_back({ source_info->name, source_info->description }); } -std::vector<AudioInput> get_pulseaudio_inputs() { - std::vector<AudioInput> inputs; +static void pa_server_info_cb(pa_context*, const pa_server_info *server_info, void *userdata) { + AudioDevices *audio_devices = (AudioDevices*)userdata; + if(server_info->default_sink_name) + audio_devices->default_output = std::string(server_info->default_sink_name) + ".monitor"; + if(server_info->default_source_name) + audio_devices->default_input = server_info->default_source_name; +} + +static void get_pulseaudio_default_inputs(AudioDevices &audio_devices) { + pa_mainloop *main_loop = pa_mainloop_new(); + + pa_context *ctx = pa_context_new(pa_mainloop_get_api(main_loop), "gpu-screen-recorder-gtk"); + 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_server_info(ctx, pa_server_info_cb, &audio_devices); + ++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; + } + + pa_mainloop_iterate(main_loop, 1, NULL); + } + + pa_mainloop_free(main_loop); +} + +AudioDevices get_pulseaudio_inputs() { + AudioDevices audio_devices; + + // TODO: Do this in the same connection below instead of two separate connections + get_pulseaudio_default_inputs(audio_devices); + pa_mainloop *main_loop = pa_mainloop_new(); pa_context *ctx = pa_context_new(pa_mainloop_get_api(main_loop), "gpu-screen-recorder"); @@ -352,7 +406,7 @@ std::vector<AudioInput> get_pulseaudio_inputs() { switch(state) { case 0: { - pa_op = pa_context_get_source_info_list(ctx, pa_sourcelist_cb, &inputs); + pa_op = pa_context_get_source_info_list(ctx, pa_sourcelist_cb, &audio_devices); ++state; break; } @@ -371,5 +425,5 @@ std::vector<AudioInput> get_pulseaudio_inputs() { } pa_mainloop_free(main_loop); - return inputs; + return audio_devices; } |