From bbc62f26021ad93560a662cb905bd6168c11ec3c Mon Sep 17 00:00:00 2001 From: fryshorts Date: Fri, 29 Aug 2014 17:06:22 +0200 Subject: [PATCH 1/5] Add function to get info on a specific source to pulse wrapper --- plugins/linux-pulseaudio/pulse-wrapper.c | 19 +++++++++++++++++++ plugins/linux-pulseaudio/pulse-wrapper.h | 19 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/plugins/linux-pulseaudio/pulse-wrapper.c b/plugins/linux-pulseaudio/pulse-wrapper.c index 44820e40e..6af459a17 100644 --- a/plugins/linux-pulseaudio/pulse-wrapper.c +++ b/plugins/linux-pulseaudio/pulse-wrapper.c @@ -181,6 +181,25 @@ int_fast32_t pulse_get_source_info_list(pa_source_info_cb_t cb, void* userdata) return 0; } +int_fast32_t pulse_get_source_info(pa_source_info_cb_t cb, const char *name, + void *userdata) +{ + if (pulse_context_ready() < 0) + return -1; + + pulse_lock(); + + pa_operation *op = pa_context_get_source_info_by_name( + pulse_context, name, cb, userdata); + while (pa_operation_get_state(op) == PA_OPERATION_RUNNING) + pulse_wait(); + pa_operation_unref(op); + + pulse_unlock(); + + return 0; +} + int_fast32_t pulse_get_server_info(pa_server_info_cb_t cb, void* userdata) { if (pulse_context_ready() < 0) diff --git a/plugins/linux-pulseaudio/pulse-wrapper.h b/plugins/linux-pulseaudio/pulse-wrapper.h index 493fc3de8..9c2a8807d 100644 --- a/plugins/linux-pulseaudio/pulse-wrapper.h +++ b/plugins/linux-pulseaudio/pulse-wrapper.h @@ -93,6 +93,25 @@ void pulse_accept(); */ int_fast32_t pulse_get_source_info_list(pa_source_info_cb_t cb, void *userdata); +/** + * Request source information from a specific source + * + * The function will block until the operation was executed and the mainloop + * called the provided callback function. + * + * @param cb pointer to the callback function + * @param name the source name to get information for + * @param userdata pointer to userdata the callback will be called with + * + * @return negative on error + * + * @note The function will block until the server context is ready. + * + * @warning call without active locks + */ +int_fast32_t pulse_get_source_info(pa_source_info_cb_t cb, const char *name, + void *userdata); + /** * Request server information * From b6482d89957546f815c37dfe76218c61263d0608 Mon Sep 17 00:00:00 2001 From: fryshorts Date: Fri, 29 Aug 2014 17:12:17 +0200 Subject: [PATCH 2/5] Use source instead of server info to get format for recording in pulse input When setting up the capture, the plugin will now query pulse for the default format of the specific source instead of the server. This is useful if a source has different settings than what the defaults are for the server, e.g. when the source is an output with 5.1 surround sound and the microphone input is mono while the server defaults to stereo sound. --- plugins/linux-pulseaudio/pulse-input.c | 31 +++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/plugins/linux-pulseaudio/pulse-input.c b/plugins/linux-pulseaudio/pulse-input.c index de241646d..fa167bbc1 100644 --- a/plugins/linux-pulseaudio/pulse-input.c +++ b/plugins/linux-pulseaudio/pulse-input.c @@ -167,21 +167,36 @@ static void pulse_server_info(pa_context *c, const pa_server_info *i, void *userdata) { UNUSED_PARAMETER(c); - PULSE_DATA(userdata); + UNUSED_PARAMETER(userdata); blog(LOG_INFO, "pulse-input: Server name: '%s %s'", i->server_name, i->server_version); + pulse_signal(0); +} + +/** + * Source info callback + */ +static void pulse_source_info(pa_context *c, const pa_source_info *i, int eol, + void *userdata) +{ + UNUSED_PARAMETER(c); + PULSE_DATA(userdata); + if (eol != 0) + goto skip; + data->format = i->sample_spec.format; data->samples_per_sec = i->sample_spec.rate; data->channels = i->sample_spec.channels; blog(LOG_INFO, "pulse-input: " - "Audio format: %s, %u Hz, %u channels", - pa_sample_format_to_string(i->sample_spec.format), - i->sample_spec.rate, - i->sample_spec.channels); + "Audio format: %s, %"PRIuFAST32" Hz, %"PRIuFAST8" channels", + pa_sample_format_to_string(data->format), + data->samples_per_sec, + data->channels); +skip: pulse_signal(0); } @@ -200,6 +215,12 @@ static int_fast32_t pulse_start_recording(struct pulse_data *data) return -1; } + if (pulse_get_source_info(pulse_source_info, data->device, + (void *) data) < 0) { + blog(LOG_ERROR, "pulse-input: Unable to get source info !"); + return -1; + } + pa_sample_spec spec; spec.format = data->format; spec.rate = data->samples_per_sec; From de3c7cafccfe83b3ee207771f54891ff5df3dd1b Mon Sep 17 00:00:00 2001 From: fryshorts Date: Fri, 29 Aug 2014 17:17:22 +0200 Subject: [PATCH 3/5] Simplify logging in pulse input Use a macro to prefix debugging messages instead of prepending manually. --- plugins/linux-pulseaudio/pulse-input.c | 35 ++++++++++++-------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/plugins/linux-pulseaudio/pulse-input.c b/plugins/linux-pulseaudio/pulse-input.c index fa167bbc1..821476064 100644 --- a/plugins/linux-pulseaudio/pulse-input.c +++ b/plugins/linux-pulseaudio/pulse-input.c @@ -22,6 +22,7 @@ along with this program. If not, see . #include "pulse-wrapper.h" #define PULSE_DATA(voidptr) struct pulse_data *data = voidptr; +#define blog(level, msg, ...) blog(level, "pulse-input: " msg, ##__VA_ARGS__) struct pulse_data { obs_source_t source; @@ -131,14 +132,14 @@ static void pulse_stream_read(pa_stream *p, size_t nbytes, void *userdata) goto exit; if (!frames) { - blog(LOG_ERROR, "pulse-input: Got audio hole of %u bytes", + blog(LOG_ERROR, "Got audio hole of %u bytes", (unsigned int) bytes); pa_stream_drop(data->stream); goto exit; } if (pulse_get_stream_latency(data->stream, &latency) < 0) { - blog(LOG_ERROR, "pulse-input: Failed to get timing info !"); + blog(LOG_ERROR, "Failed to get timing info !"); pa_stream_drop(data->stream); goto exit; } @@ -169,7 +170,7 @@ static void pulse_server_info(pa_context *c, const pa_server_info *i, UNUSED_PARAMETER(c); UNUSED_PARAMETER(userdata); - blog(LOG_INFO, "pulse-input: Server name: '%s %s'", + blog(LOG_INFO, "Server name: '%s %s'", i->server_name, i->server_version); pulse_signal(0); @@ -190,8 +191,8 @@ static void pulse_source_info(pa_context *c, const pa_source_info *i, int eol, data->samples_per_sec = i->sample_spec.rate; data->channels = i->sample_spec.channels; - blog(LOG_INFO, "pulse-input: " - "Audio format: %s, %"PRIuFAST32" Hz, %"PRIuFAST8" channels", + blog(LOG_INFO, "Audio format: %s, %"PRIuFAST32" Hz" + ", %"PRIuFAST8" channels", pa_sample_format_to_string(data->format), data->samples_per_sec, data->channels); @@ -211,13 +212,13 @@ skip: static int_fast32_t pulse_start_recording(struct pulse_data *data) { if (pulse_get_server_info(pulse_server_info, (void *) data) < 0) { - blog(LOG_ERROR, "pulse-input: Unable to get server info !"); + blog(LOG_ERROR, "Unable to get server info !"); return -1; } if (pulse_get_source_info(pulse_source_info, data->device, (void *) data) < 0) { - blog(LOG_ERROR, "pulse-input: Unable to get source info !"); + blog(LOG_ERROR, "Unable to get source info !"); return -1; } @@ -227,7 +228,7 @@ static int_fast32_t pulse_start_recording(struct pulse_data *data) spec.channels = data->channels; if (!pa_sample_spec_valid(&spec)) { - blog(LOG_ERROR, "pulse-input: Sample spec is not valid"); + blog(LOG_ERROR, "Sample spec is not valid"); return -1; } @@ -237,7 +238,7 @@ static int_fast32_t pulse_start_recording(struct pulse_data *data) data->stream = pulse_stream_new(obs_source_get_name(data->source), &spec, NULL); if (!data->stream) { - blog(LOG_ERROR, "pulse-input: Unable to create stream"); + blog(LOG_ERROR, "Unable to create stream"); return -1; } @@ -261,12 +262,11 @@ static int_fast32_t pulse_start_recording(struct pulse_data *data) pulse_unlock(); if (ret < 0) { pulse_stop_recording(data); - blog(LOG_ERROR, "pulse-input: Unable to connect to stream"); + blog(LOG_ERROR, "Unable to connect to stream"); return -1; } - blog(LOG_INFO, "pulse-input: Started recording from '%s'", - data->device); + blog(LOG_INFO, "Started recording from '%s'", data->device); return 0; } @@ -283,10 +283,8 @@ static void pulse_stop_recording(struct pulse_data *data) pulse_unlock(); } - blog(LOG_INFO, "pulse-input: Stopped recording from '%s'", - data->device); - blog(LOG_INFO, "pulse-input: Got %"PRIuFAST32 - " packets with %"PRIuFAST64" frames", + blog(LOG_INFO, "Stopped recording from '%s'", data->device); + blog(LOG_INFO, "Got %"PRIuFAST32" packets with %"PRIuFAST64" frames", data->packets, data->frames); data->packets = 0; data->frames = 0; @@ -365,8 +363,7 @@ static void pulse_input_device(pa_context *c, const pa_server_info *i, obs_data_set_default_string(settings, "device_id", i->default_source_name); - blog(LOG_DEBUG, "pulse-input: Default input device: '%s'", - i->default_source_name); + blog(LOG_DEBUG, "Default input device: '%s'", i->default_source_name); pulse_signal(0); } @@ -382,7 +379,7 @@ static void pulse_output_device(pa_context *c, const pa_server_info *i, strcat(monitor, ".monitor"); obs_data_set_default_string(settings, "device_id", monitor); - blog(LOG_DEBUG, "pulse-input: Default output device: '%s'", monitor); + blog(LOG_DEBUG, "Default output device: '%s'", monitor); bfree(monitor); pulse_signal(0); From 164c2e01b9732889d8ee0bb7d8f320b04598ff7c Mon Sep 17 00:00:00 2001 From: fryshorts Date: Fri, 29 Aug 2014 17:19:40 +0200 Subject: [PATCH 4/5] Small coding style fixes in pulse input --- plugins/linux-pulseaudio/pulse-input.c | 29 +++++++++++--------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/plugins/linux-pulseaudio/pulse-input.c b/plugins/linux-pulseaudio/pulse-input.c index 821476064..6a3c5a495 100644 --- a/plugins/linux-pulseaudio/pulse-input.c +++ b/plugins/linux-pulseaudio/pulse-input.c @@ -52,16 +52,11 @@ static enum audio_format pulse_to_obs_audio_format( pa_sample_format_t format) { switch (format) { - case PA_SAMPLE_U8: - return AUDIO_FORMAT_U8BIT; - case PA_SAMPLE_S16LE: - return AUDIO_FORMAT_16BIT; - case PA_SAMPLE_S24_32LE: - return AUDIO_FORMAT_32BIT; - case PA_SAMPLE_FLOAT32LE: - return AUDIO_FORMAT_FLOAT; - default: - return AUDIO_FORMAT_UNKNOWN; + case PA_SAMPLE_U8: return AUDIO_FORMAT_U8BIT; + case PA_SAMPLE_S16LE: return AUDIO_FORMAT_16BIT; + case PA_SAMPLE_S24_32LE: return AUDIO_FORMAT_32BIT; + case PA_SAMPLE_FLOAT32LE: return AUDIO_FORMAT_FLOAT; + default: return AUDIO_FORMAT_UNKNOWN; } return AUDIO_FORMAT_UNKNOWN; @@ -81,13 +76,13 @@ static enum speaker_layout pulse_channels_to_obs_speakers( uint_fast32_t channels) { switch(channels) { - case 1: return SPEAKERS_MONO; - case 2: return SPEAKERS_STEREO; - case 3: return SPEAKERS_2POINT1; - case 4: return SPEAKERS_SURROUND; - case 5: return SPEAKERS_4POINT1; - case 6: return SPEAKERS_5POINT1; - case 8: return SPEAKERS_7POINT1; + case 1: return SPEAKERS_MONO; + case 2: return SPEAKERS_STEREO; + case 3: return SPEAKERS_2POINT1; + case 4: return SPEAKERS_SURROUND; + case 5: return SPEAKERS_4POINT1; + case 6: return SPEAKERS_5POINT1; + case 8: return SPEAKERS_7POINT1; } return SPEAKERS_UNKNOWN; From 2fca3b38e8c14491a3c1848ebf70597ad1fae62a Mon Sep 17 00:00:00 2001 From: fryshorts Date: Tue, 2 Sep 2014 19:35:17 +0200 Subject: [PATCH 5/5] Log average latency in pulse input This adds another statistics counter to the pulse plugin in order to print the average latency reported by pulse when the source is destroyed. --- plugins/linux-pulseaudio/pulse-input.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/plugins/linux-pulseaudio/pulse-input.c b/plugins/linux-pulseaudio/pulse-input.c index 6a3c5a495..39cc47d4c 100644 --- a/plugins/linux-pulseaudio/pulse-input.c +++ b/plugins/linux-pulseaudio/pulse-input.c @@ -41,6 +41,7 @@ struct pulse_data { /* statistics */ uint_fast32_t packets; uint_fast64_t frames; + double latency; }; static void pulse_stop_recording(struct pulse_data *data); @@ -150,6 +151,7 @@ static void pulse_stream_read(pa_stream *p, size_t nbytes, void *userdata) data->packets++; data->frames += out.frames; + data->latency += latency; pa_stream_drop(data->stream); exit: @@ -278,11 +280,16 @@ static void pulse_stop_recording(struct pulse_data *data) pulse_unlock(); } + data->latency /= (double) data->packets * 1000.0; + blog(LOG_INFO, "Stopped recording from '%s'", data->device); blog(LOG_INFO, "Got %"PRIuFAST32" packets with %"PRIuFAST64" frames", data->packets, data->frames); + blog(LOG_INFO, "Average latency: %.2f msec", data->latency); + data->packets = 0; data->frames = 0; + data->latency = 0.0; } /**