mirror of
https://repo.dec05eba.com/gpu-screen-recorder
synced 2026-03-31 09:07:13 +09:00
Fix virtual sink not destroyed if gsr is forcefully killed (use pipewire to create the virtual sink instead of pulseaudio)
This commit is contained in:
@@ -11,6 +11,7 @@
|
|||||||
#define GSR_PIPEWIRE_AUDIO_MAX_STREAM_NODES 128
|
#define GSR_PIPEWIRE_AUDIO_MAX_STREAM_NODES 128
|
||||||
#define GSR_PIPEWIRE_AUDIO_MAX_PORTS 256
|
#define GSR_PIPEWIRE_AUDIO_MAX_PORTS 256
|
||||||
#define GSR_PIPEWIRE_AUDIO_MAX_REQUESTED_LINKS 32
|
#define GSR_PIPEWIRE_AUDIO_MAX_REQUESTED_LINKS 32
|
||||||
|
#define GSR_PIPEWIRE_AUDIO_MAX_VIRTUAL_SINKS 32
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
GSR_PIPEWIRE_AUDIO_NODE_TYPE_STREAM_OUTPUT, /* Application audio */
|
GSR_PIPEWIRE_AUDIO_NODE_TYPE_STREAM_OUTPUT, /* Application audio */
|
||||||
@@ -67,11 +68,16 @@ typedef struct {
|
|||||||
|
|
||||||
gsr_pipewire_audio_requested_link requested_links[GSR_PIPEWIRE_AUDIO_MAX_REQUESTED_LINKS];
|
gsr_pipewire_audio_requested_link requested_links[GSR_PIPEWIRE_AUDIO_MAX_REQUESTED_LINKS];
|
||||||
int num_requested_links;
|
int num_requested_links;
|
||||||
|
|
||||||
|
struct pw_proxy *virtual_sink_proxies[GSR_PIPEWIRE_AUDIO_MAX_VIRTUAL_SINKS];
|
||||||
|
int num_virtual_sink_proxies;
|
||||||
} gsr_pipewire_audio;
|
} gsr_pipewire_audio;
|
||||||
|
|
||||||
bool gsr_pipewire_audio_init(gsr_pipewire_audio *self);
|
bool gsr_pipewire_audio_init(gsr_pipewire_audio *self);
|
||||||
void gsr_pipewire_audio_deinit(gsr_pipewire_audio *self);
|
void gsr_pipewire_audio_deinit(gsr_pipewire_audio *self);
|
||||||
|
|
||||||
|
bool gsr_pipewire_audio_create_virtual_sink(gsr_pipewire_audio *self, const char *name);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This function links audio source outputs from applications that match the name |app_names| to the input
|
This function links audio source outputs from applications that match the name |app_names| to the input
|
||||||
that matches the name |stream_name_input|.
|
that matches the name |stream_name_input|.
|
||||||
|
|||||||
@@ -64,11 +64,6 @@ typedef enum {
|
|||||||
Returns 0 on success, or a negative value on failure.
|
Returns 0 on success, or a negative value on failure.
|
||||||
*/
|
*/
|
||||||
int sound_device_get_by_name(SoundDevice *device, const char *device_name, const char *description, unsigned int num_channels, unsigned int period_frame_size, AudioFormat audio_format);
|
int sound_device_get_by_name(SoundDevice *device, const char *device_name, const char *description, unsigned int num_channels, unsigned int period_frame_size, AudioFormat audio_format);
|
||||||
/*
|
|
||||||
Creates a module-combine-sink and connects to it for recording, returning the device into the |device| parameter.
|
|
||||||
Returns 0 on success, or a negative value on failure.
|
|
||||||
*/
|
|
||||||
int sound_device_create_combined_sink_connect(SoundDevice *device, const char *combined_sink_name, unsigned int num_channels, unsigned int period_frame_size, AudioFormat audio_format);
|
|
||||||
|
|
||||||
void sound_device_close(SoundDevice *device);
|
void sound_device_close(SoundDevice *device);
|
||||||
|
|
||||||
|
|||||||
28
src/main.cpp
28
src/main.cpp
@@ -1403,7 +1403,6 @@ struct AudioDeviceData {
|
|||||||
AVFilterContext *src_filter_ctx = nullptr;
|
AVFilterContext *src_filter_ctx = nullptr;
|
||||||
AVFrame *frame = nullptr;
|
AVFrame *frame = nullptr;
|
||||||
std::thread thread; // TODO: Instead of having a thread for each track, have one thread for all threads and read the data with non-blocking read
|
std::thread thread; // TODO: Instead of having a thread for each track, have one thread for all threads and read the data with non-blocking read
|
||||||
std::string combined_sink_name;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: Cleanup
|
// TODO: Cleanup
|
||||||
@@ -2885,21 +2884,28 @@ static AudioDeviceData create_application_audio_audio_input(const MergedAudioInp
|
|||||||
|
|
||||||
char random_str[8];
|
char random_str[8];
|
||||||
if(!generate_random_characters_standard_alphabet(random_str, sizeof(random_str))) {
|
if(!generate_random_characters_standard_alphabet(random_str, sizeof(random_str))) {
|
||||||
fprintf(stderr, "gsr error: ailed to generate random string\n");
|
fprintf(stderr, "gsr error: failed to generate random string\n");
|
||||||
_exit(1);
|
_exit(1);
|
||||||
}
|
}
|
||||||
audio_device.combined_sink_name = "gsr-combined-";
|
std::string combined_sink_name = "gsr-combined-";
|
||||||
audio_device.combined_sink_name.append(random_str, sizeof(random_str));
|
combined_sink_name.append(random_str, sizeof(random_str));
|
||||||
|
|
||||||
if(sound_device_create_combined_sink_connect(&audio_device.sound_device, audio_device.combined_sink_name.c_str(), num_channels, audio_codec_context->frame_size, audio_codec_context_get_audio_format(audio_codec_context)) != 0) {
|
if(!gsr_pipewire_audio_create_virtual_sink(pipewire_audio, combined_sink_name.c_str())) {
|
||||||
|
fprintf(stderr, "gsr error: failed to create virtual sink for application audio\n");
|
||||||
|
_exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
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, "Error: failed to setup audio recording to combined sink\n");
|
||||||
_exit(1);
|
_exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<const char*> audio_sources;
|
std::vector<const char*> audio_devices_sources;
|
||||||
for(const auto &audio_input : merged_audio_inputs.audio_inputs) {
|
for(const auto &audio_input : merged_audio_inputs.audio_inputs) {
|
||||||
if(audio_input.type == AudioInputType::DEVICE)
|
if(audio_input.type == AudioInputType::DEVICE)
|
||||||
audio_sources.push_back(audio_input.name.c_str());
|
audio_devices_sources.push_back(audio_input.name.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool app_audio_inverted = false;
|
bool app_audio_inverted = false;
|
||||||
@@ -2911,20 +2917,20 @@ static AudioDeviceData create_application_audio_audio_input(const MergedAudioInp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!audio_sources.empty()) {
|
if(!audio_devices_sources.empty()) {
|
||||||
if(!gsr_pipewire_audio_add_link_from_sources_to_sink(pipewire_audio, audio_sources.data(), audio_sources.size(), audio_device.combined_sink_name.c_str())) {
|
if(!gsr_pipewire_audio_add_link_from_sources_to_sink(pipewire_audio, audio_devices_sources.data(), audio_devices_sources.size(), combined_sink_name.c_str())) {
|
||||||
fprintf(stderr, "gsr error: failed to add application audio link\n");
|
fprintf(stderr, "gsr error: failed to add application audio link\n");
|
||||||
_exit(1);
|
_exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(app_audio_inverted) {
|
if(app_audio_inverted) {
|
||||||
if(!gsr_pipewire_audio_add_link_from_apps_to_sink_inverted(pipewire_audio, app_names.data(), app_names.size(), audio_device.combined_sink_name.c_str())) {
|
if(!gsr_pipewire_audio_add_link_from_apps_to_sink_inverted(pipewire_audio, app_names.data(), app_names.size(), combined_sink_name.c_str())) {
|
||||||
fprintf(stderr, "gsr error: failed to add application audio link\n");
|
fprintf(stderr, "gsr error: failed to add application audio link\n");
|
||||||
_exit(1);
|
_exit(1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(!gsr_pipewire_audio_add_link_from_apps_to_sink(pipewire_audio, app_names.data(), app_names.size(), audio_device.combined_sink_name.c_str())) {
|
if(!gsr_pipewire_audio_add_link_from_apps_to_sink(pipewire_audio, app_names.data(), app_names.size(), combined_sink_name.c_str())) {
|
||||||
fprintf(stderr, "gsr error: failed to add application audio link\n");
|
fprintf(stderr, "gsr error: failed to add application audio link\n");
|
||||||
_exit(1);
|
_exit(1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -277,20 +277,20 @@ bool gsr_pipewire_audio_init(gsr_pipewire_audio *self) {
|
|||||||
|
|
||||||
self->thread_loop = pw_thread_loop_new("gsr screen capture", NULL);
|
self->thread_loop = pw_thread_loop_new("gsr screen capture", NULL);
|
||||||
if(!self->thread_loop) {
|
if(!self->thread_loop) {
|
||||||
fprintf(stderr, "gsr error: gsr_pipewire_video_setup_stream: failed to create pipewire thread\n");
|
fprintf(stderr, "gsr error: gsr_pipewire_audio_init: failed to create pipewire thread\n");
|
||||||
gsr_pipewire_audio_deinit(self);
|
gsr_pipewire_audio_deinit(self);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
self->context = pw_context_new(pw_thread_loop_get_loop(self->thread_loop), NULL, 0);
|
self->context = pw_context_new(pw_thread_loop_get_loop(self->thread_loop), NULL, 0);
|
||||||
if(!self->context) {
|
if(!self->context) {
|
||||||
fprintf(stderr, "gsr error: gsr_pipewire_video_setup_stream: failed to create pipewire context\n");
|
fprintf(stderr, "gsr error: gsr_pipewire_audio_init: failed to create pipewire context\n");
|
||||||
gsr_pipewire_audio_deinit(self);
|
gsr_pipewire_audio_deinit(self);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(pw_thread_loop_start(self->thread_loop) < 0) {
|
if(pw_thread_loop_start(self->thread_loop) < 0) {
|
||||||
fprintf(stderr, "gsr error: gsr_pipewire_video_setup_stream: failed to start thread\n");
|
fprintf(stderr, "gsr error: gsr_pipewire_audio_init: failed to start thread\n");
|
||||||
gsr_pipewire_audio_deinit(self);
|
gsr_pipewire_audio_deinit(self);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -312,7 +312,6 @@ bool gsr_pipewire_audio_init(gsr_pipewire_audio *self) {
|
|||||||
|
|
||||||
self->server_version_sync = pw_core_sync(self->core, PW_ID_CORE, 0);
|
self->server_version_sync = pw_core_sync(self->core, PW_ID_CORE, 0);
|
||||||
pw_thread_loop_wait(self->thread_loop);
|
pw_thread_loop_wait(self->thread_loop);
|
||||||
|
|
||||||
pw_thread_loop_unlock(self->thread_loop);
|
pw_thread_loop_unlock(self->thread_loop);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -323,6 +322,14 @@ void gsr_pipewire_audio_deinit(gsr_pipewire_audio *self) {
|
|||||||
pw_thread_loop_stop(self->thread_loop);
|
pw_thread_loop_stop(self->thread_loop);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for(int 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;
|
||||||
|
|
||||||
if(self->core) {
|
if(self->core) {
|
||||||
pw_core_disconnect(self->core);
|
pw_core_disconnect(self->core);
|
||||||
self->core = NULL;
|
self->core = NULL;
|
||||||
@@ -362,6 +369,55 @@ void gsr_pipewire_audio_deinit(gsr_pipewire_audio *self) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct pw_properties* gsr_pipewire_create_null_audio_sink(const char *name) {
|
||||||
|
struct spa_error_location err_loc;
|
||||||
|
char props_str[512];
|
||||||
|
snprintf(props_str, sizeof(props_str), "{ factory.name=support.null-audio-sink node.name=\"%s\" media.class=Audio/Sink object.linger=false audio.position=[FL FR] monitor.channel-volumes=true monitor.passthrough=true adjust_time=0 slaves=\"\" }", name);
|
||||||
|
struct pw_properties *props = pw_properties_new_string_checked(props_str, strlen(props_str), &err_loc);
|
||||||
|
if(!props) {
|
||||||
|
fprintf(stderr, "gsr error: gsr_pipewire_create_null_audio_sink: failed to create virtual sink properties, error: %d:%d: %s\n", err_loc.line, err_loc.col, err_loc.reason);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return props;
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pw_thread_loop_lock(self->thread_loop);
|
||||||
|
|
||||||
|
struct pw_properties *virtual_sink_props = gsr_pipewire_create_null_audio_sink(name);
|
||||||
|
if(!virtual_sink_props) {
|
||||||
|
pw_thread_loop_unlock(self->thread_loop);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct pw_proxy *virtual_sink_proxy = pw_core_create_object(self->core, "adapter", PW_TYPE_INTERFACE_Node, PW_VERSION_NODE, &virtual_sink_props->dict, 0);
|
||||||
|
// TODO:
|
||||||
|
// If these are done then the above needs sizeof(*self) as the last argument
|
||||||
|
//pw_proxy_add_object_listener(virtual_sink_proxy, &pd->object_listener, &node_events, self);
|
||||||
|
//pw_proxy_add_listener(virtual_sink_proxy, &pd->proxy_listener, &proxy_events, self);
|
||||||
|
// TODO: proxy
|
||||||
|
pw_properties_free(virtual_sink_props);
|
||||||
|
if(!virtual_sink_proxy) {
|
||||||
|
fprintf(stderr, "gsr error: gsr_pipewire_audio_create_virtual_sink: failed to create virtual sink\n");
|
||||||
|
pw_thread_loop_unlock(self->thread_loop);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
self->server_version_sync = pw_core_sync(self->core, PW_ID_CORE, self->server_version_sync);
|
||||||
|
pw_thread_loop_wait(self->thread_loop);
|
||||||
|
pw_thread_loop_unlock(self->thread_loop);
|
||||||
|
|
||||||
|
self->virtual_sink_proxies[self->num_virtual_sink_proxies] = virtual_sink_proxy;
|
||||||
|
++self->num_virtual_sink_proxies;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static bool string_remove_suffix(char *str, const char *suffix) {
|
static bool string_remove_suffix(char *str, const char *suffix) {
|
||||||
int str_len = strlen(str);
|
int str_len = strlen(str);
|
||||||
int suffix_len = strlen(suffix);
|
int suffix_len = strlen(suffix);
|
||||||
@@ -387,6 +443,9 @@ static bool gsr_pipewire_audio_add_link_from_apps_to_output(gsr_pipewire_audio *
|
|||||||
if(!input_name_copy)
|
if(!input_name_copy)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
|
if(input_type == GSR_PIPEWIRE_AUDIO_LINK_INPUT_TYPE_SINK)
|
||||||
|
string_remove_suffix(input_name_copy, ".monitor");
|
||||||
|
|
||||||
for(int i = 0; i < num_output_names; ++i) {
|
for(int i = 0; i < num_output_names; ++i) {
|
||||||
output_names_copy[i] = strdup(output_names[i]);
|
output_names_copy[i] = strdup(output_names[i]);
|
||||||
if(!output_names_copy[i])
|
if(!output_names_copy[i])
|
||||||
|
|||||||
@@ -42,30 +42,11 @@ struct pa_handle {
|
|||||||
|
|
||||||
int operation_success;
|
int operation_success;
|
||||||
double latency_seconds;
|
double latency_seconds;
|
||||||
|
|
||||||
uint32_t combined_sink_module_index;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static void destroy_combined_sink(pa_handle *p) {
|
|
||||||
// TODO: error handling
|
|
||||||
pa_operation *module_pa = pa_context_unload_module(p->context, p->combined_sink_module_index, NULL, NULL);
|
|
||||||
for(;;) {
|
|
||||||
if(pa_operation_get_state(module_pa) == PA_OPERATION_DONE) {
|
|
||||||
pa_operation_unref(module_pa);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
pa_mainloop_iterate(p->mainloop, 1, NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pa_sound_device_free(pa_handle *p) {
|
static void pa_sound_device_free(pa_handle *p) {
|
||||||
assert(p);
|
assert(p);
|
||||||
|
|
||||||
if(p->combined_sink_module_index != PA_INVALID_INDEX) {
|
|
||||||
destroy_combined_sink(p);
|
|
||||||
p->combined_sink_module_index = PA_INVALID_INDEX;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p->stream) {
|
if (p->stream) {
|
||||||
pa_stream_unref(p->stream);
|
pa_stream_unref(p->stream);
|
||||||
p->stream = NULL;
|
p->stream = NULL;
|
||||||
@@ -90,31 +71,10 @@ static void pa_sound_device_free(pa_handle *p) {
|
|||||||
pa_xfree(p);
|
pa_xfree(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void module_index_callback(pa_context*, uint32_t idx, void *userdata) {
|
|
||||||
pa_handle *p = (pa_handle*)userdata;
|
|
||||||
p->combined_sink_module_index = idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool create_null_sink(pa_handle *p, const char *null_sink_name) {
|
|
||||||
// TODO: Error handling
|
|
||||||
char module_argument[256];
|
|
||||||
snprintf(module_argument, sizeof(module_argument), "sink_name=\"%s\" slaves= adjust_time=0", null_sink_name);
|
|
||||||
pa_operation *module_pa = pa_context_load_module(p->context, "module-null-sink", module_argument, module_index_callback, p);
|
|
||||||
for(;;) {
|
|
||||||
if(pa_operation_get_state(module_pa) == PA_OPERATION_DONE) {
|
|
||||||
pa_operation_unref(module_pa);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
pa_mainloop_iterate(p->mainloop, 1, NULL);
|
|
||||||
}
|
|
||||||
return p->combined_sink_module_index != PA_INVALID_INDEX;
|
|
||||||
}
|
|
||||||
|
|
||||||
static pa_handle* pa_sound_device_new(const char *server,
|
static pa_handle* pa_sound_device_new(const char *server,
|
||||||
const char *name,
|
const char *name,
|
||||||
const char *dev,
|
const char *dev,
|
||||||
const char *stream_name,
|
const char *stream_name,
|
||||||
const char *combined_sink_name,
|
|
||||||
const pa_sample_spec *ss,
|
const pa_sample_spec *ss,
|
||||||
const pa_buffer_attr *attr,
|
const pa_buffer_attr *attr,
|
||||||
int *rerror) {
|
int *rerror) {
|
||||||
@@ -122,7 +82,6 @@ static pa_handle* pa_sound_device_new(const char *server,
|
|||||||
int error = PA_ERR_INTERNAL, r;
|
int error = PA_ERR_INTERNAL, r;
|
||||||
|
|
||||||
p = pa_xnew0(pa_handle, 1);
|
p = pa_xnew0(pa_handle, 1);
|
||||||
p->combined_sink_module_index = PA_INVALID_INDEX;
|
|
||||||
|
|
||||||
const int buffer_size = attr->fragsize;
|
const int buffer_size = attr->fragsize;
|
||||||
void *buffer = malloc(buffer_size);
|
void *buffer = malloc(buffer_size);
|
||||||
@@ -161,23 +120,12 @@ static pa_handle* pa_sound_device_new(const char *server,
|
|||||||
pa_mainloop_iterate(p->mainloop, 1, NULL);
|
pa_mainloop_iterate(p->mainloop, 1, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
char device_to_record[256];
|
|
||||||
if(combined_sink_name) {
|
|
||||||
if(!create_null_sink(p, combined_sink_name)) {
|
|
||||||
fprintf(stderr, "gsr error: pa_sound_device_new: failed to create module-combine-sink\n");
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
snprintf(device_to_record, sizeof(device_to_record), "%s.monitor", combined_sink_name);
|
|
||||||
} else {
|
|
||||||
snprintf(device_to_record, sizeof(device_to_record), "%s", dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(p->stream = pa_stream_new(p->context, stream_name, ss, NULL))) {
|
if (!(p->stream = pa_stream_new(p->context, stream_name, ss, NULL))) {
|
||||||
error = pa_context_errno(p->context);
|
error = pa_context_errno(p->context);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = pa_stream_connect_record(p->stream, device_to_record, attr,
|
r = pa_stream_connect_record(p->stream, dev, attr,
|
||||||
(pa_stream_flags_t)(PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_ADJUST_LATENCY|PA_STREAM_AUTO_TIMING_UPDATE));
|
(pa_stream_flags_t)(PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_ADJUST_LATENCY|PA_STREAM_AUTO_TIMING_UPDATE));
|
||||||
|
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
@@ -312,7 +260,7 @@ static int audio_format_to_get_bytes_per_sample(AudioFormat audio_format) {
|
|||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sound_device_setup_record(SoundDevice *device, const char *device_name, const char *description, unsigned int num_channels, unsigned int period_frame_size, AudioFormat audio_format, const char *combined_sink_name) {
|
int sound_device_get_by_name(SoundDevice *device, const char *device_name, const char *description, unsigned int num_channels, unsigned int period_frame_size, AudioFormat audio_format) {
|
||||||
pa_sample_spec ss;
|
pa_sample_spec ss;
|
||||||
ss.format = audio_format_to_pulse_audio_format(audio_format);
|
ss.format = audio_format_to_pulse_audio_format(audio_format);
|
||||||
ss.rate = 48000;
|
ss.rate = 48000;
|
||||||
@@ -326,7 +274,7 @@ static int sound_device_setup_record(SoundDevice *device, const char *device_nam
|
|||||||
buffer_attr.maxlength = buffer_attr.fragsize;
|
buffer_attr.maxlength = buffer_attr.fragsize;
|
||||||
|
|
||||||
int error = 0;
|
int error = 0;
|
||||||
pa_handle *handle = pa_sound_device_new(nullptr, description, device_name, description, combined_sink_name, &ss, &buffer_attr, &error);
|
pa_handle *handle = pa_sound_device_new(nullptr, description, device_name, description, &ss, &buffer_attr, &error);
|
||||||
if(!handle) {
|
if(!handle) {
|
||||||
fprintf(stderr, "pa_sound_device_new() failed: %s. Audio input device %s might not be valid\n", pa_strerror(error), description);
|
fprintf(stderr, "pa_sound_device_new() failed: %s. Audio input device %s might not be valid\n", pa_strerror(error), description);
|
||||||
return -1;
|
return -1;
|
||||||
@@ -337,14 +285,6 @@ static int sound_device_setup_record(SoundDevice *device, const char *device_nam
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sound_device_get_by_name(SoundDevice *device, const char *device_name, const char *description, unsigned int num_channels, unsigned int period_frame_size, AudioFormat audio_format) {
|
|
||||||
return sound_device_setup_record(device, device_name, description, num_channels, period_frame_size, audio_format, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
int sound_device_create_combined_sink_connect(SoundDevice *device, const char *combined_sink_name, unsigned int num_channels, unsigned int period_frame_size, AudioFormat audio_format) {
|
|
||||||
return sound_device_setup_record(device, "gpu-screen-recorder", "gpu-screen-recorder", num_channels, period_frame_size, audio_format, combined_sink_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sound_device_close(SoundDevice *device) {
|
void sound_device_close(SoundDevice *device) {
|
||||||
if(device->handle)
|
if(device->handle)
|
||||||
pa_sound_device_free((pa_handle*)device->handle);
|
pa_sound_device_free((pa_handle*)device->handle);
|
||||||
|
|||||||
Reference in New Issue
Block a user