diff --git a/plugins/linux-pulseaudio/pulse-input.c b/plugins/linux-pulseaudio/pulse-input.c
index 8c920ead1b44c394c7c903778ebc25bc3ed2bffa..eaf6d900a5711fddce02f129fc3dff306b19c285 100644
--- a/plugins/linux-pulseaudio/pulse-input.c
+++ b/plugins/linux-pulseaudio/pulse-input.c
@@ -15,6 +15,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
+#include
#include
#include
@@ -34,9 +35,13 @@ struct pulse_data {
uint_fast32_t bytes_per_frame;
pa_stream *stream;
+
+ bool ostime;
};
-/*
+static void pulse_stop_recording(struct pulse_data *data);
+
+/**
* get obs from pulse audio format
*/
static enum audio_format pulse_to_obs_audio_format(
@@ -58,8 +63,8 @@ static enum audio_format pulse_to_obs_audio_format(
return AUDIO_FORMAT_UNKNOWN;
}
-/*
- * get the buffer size needed for length msec with current settings
+/**
+ * Get the buffer size needed for length msec with current settings
*/
static uint_fast32_t get_buffer_size(struct pulse_data *data,
uint_fast32_t length)
@@ -67,7 +72,7 @@ static uint_fast32_t get_buffer_size(struct pulse_data *data,
return (length * data->samples_per_sec * data->bytes_per_frame) / 1000;
}
-/*
+/**
* Get latency for a pulse audio stream
*/
static int pulse_get_stream_latency(pa_stream *stream, int64_t *latency)
@@ -81,8 +86,10 @@ static int pulse_get_stream_latency(pa_stream *stream, int64_t *latency)
return ret;
}
-/*
+/**
* Callback for pulse which gets executed when new audio data is available
+ *
+ * @warning The function may be called even after disconnecting the stream
*/
static void pulse_stream_read(pa_stream *p, size_t nbytes, void *userdata)
{
@@ -95,6 +102,9 @@ static void pulse_stream_read(pa_stream *p, size_t nbytes, void *userdata)
uint64_t pa_time;
int64_t pa_latency;
+ if (!data->stream)
+ goto exit;
+
pa_stream_peek(data->stream, &frames, &bytes);
// check if we got data
@@ -115,6 +125,7 @@ static void pulse_stream_read(pa_stream *p, size_t nbytes, void *userdata)
pa_stream_drop(data->stream);
goto exit;
}
+ pa_time = (!data->ostime) ? pa_time * 1000 : os_gettime_ns();
pulse_get_stream_latency(data->stream, &pa_latency);
@@ -124,7 +135,7 @@ static void pulse_stream_read(pa_stream *p, size_t nbytes, void *userdata)
out.format = pulse_to_obs_audio_format(data->format);
out.data[0] = (uint8_t *) frames;
out.frames = bytes / data->bytes_per_frame;
- out.timestamp = (pa_time - pa_latency) * 1000;
+ out.timestamp = pa_time - (pa_latency * 1000);
obs_source_output_audio(data->source, &out);
pa_stream_drop(data->stream);
@@ -133,7 +144,7 @@ exit:
pulse_signal(0);
}
-/*
+/**
* Server info callback
*/
static void pulse_server_info(pa_context *c, const pa_server_info *i,
@@ -154,7 +165,7 @@ static void pulse_server_info(pa_context *c, const pa_server_info *i,
pulse_signal(0);
}
-/*
+/**
* start recording
*/
static int_fast32_t pulse_start_recording(struct pulse_data *data)
@@ -207,6 +218,7 @@ static int_fast32_t pulse_start_recording(struct pulse_data *data)
&attr, flags);
pulse_unlock();
if (ret < 0) {
+ pulse_stop_recording(data);
blog(LOG_ERROR, "pulse-input: Unable to connect to stream");
return -1;
}
@@ -215,7 +227,7 @@ static int_fast32_t pulse_start_recording(struct pulse_data *data)
return 0;
}
-/*
+/**
* stop recording
*/
static void pulse_stop_recording(struct pulse_data *data)
@@ -224,11 +236,12 @@ static void pulse_stop_recording(struct pulse_data *data)
pulse_lock();
pa_stream_disconnect(data->stream);
pa_stream_unref(data->stream);
+ data->stream = NULL;
pulse_unlock();
}
}
-/*
+/**
* input info callback
*/
static void pulse_input_info(pa_context *c, const pa_source_info *i, int eol,
@@ -262,8 +275,8 @@ skip:
pulse_signal(0);
}
-/*
- * get plugin properties
+/**
+ * Get plugin properties
*/
static obs_properties_t pulse_properties(const char *locale, bool input)
{
@@ -276,6 +289,8 @@ static obs_properties_t pulse_properties(const char *locale, bool input)
pulse_get_source_info_list(cb, (void *) devices);
pulse_unref();
+ obs_properties_add_bool(props, "ostime", "Use OS timestamps");
+
return props;
}
@@ -289,15 +304,67 @@ static obs_properties_t pulse_output_properties(const char *locale)
return pulse_properties(locale, false);
}
-/*
- * get plugin defaults
+/**
+ * Server info callback
*/
-static void pulse_defaults(obs_data_t settings)
+static void pulse_input_device(pa_context *c, const pa_server_info *i,
+ void *userdata)
{
- obs_data_set_default_string(settings, "device_id", "default");
+ UNUSED_PARAMETER(c);
+ obs_data_t settings = (obs_data_t) userdata;
+
+ 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);
+
+ pulse_signal(0);
}
-/*
+static void pulse_output_device(pa_context *c, const pa_server_info *i,
+ void *userdata)
+{
+ UNUSED_PARAMETER(c);
+ obs_data_t settings = (obs_data_t) userdata;
+
+ char *monitor = bzalloc(strlen(i->default_sink_name) + 9);
+ strcat(monitor, i->default_sink_name);
+ strcat(monitor, ".monitor");
+
+ obs_data_set_default_string(settings, "device_id", monitor);
+ blog(LOG_DEBUG, "pulse-input: Default output device: '%s'", monitor);
+ bfree(monitor);
+
+ pulse_signal(0);
+}
+
+/**
+ * Get plugin defaults
+ */
+static void pulse_defaults(obs_data_t settings, bool input)
+{
+ pulse_init();
+
+ pa_server_info_cb_t cb = (input)
+ ? pulse_input_device : pulse_output_device;
+ pulse_get_server_info(cb, (void *) settings);
+
+ pulse_unref();
+
+ obs_data_set_default_bool(settings, "ostime", false);
+}
+
+static void pulse_input_defaults(obs_data_t settings)
+{
+ return pulse_defaults(settings, true);
+}
+
+static void pulse_output_defaults(obs_data_t settings)
+{
+ return pulse_defaults(settings, false);
+}
+
+/**
* Returns the name of the plugin
*/
static const char *pulse_input_getname(const char *locale)
@@ -312,7 +379,7 @@ static const char *pulse_output_getname(const char *locale)
return "Pulse Audio Output Capture";
}
-/*
+/**
* Destroy the plugin object and free all memory
*/
static void pulse_destroy(void *vptr)
@@ -322,7 +389,8 @@ static void pulse_destroy(void *vptr)
if (!data)
return;
- pulse_stop_recording(data);
+ if (data->stream)
+ pulse_stop_recording(data);
pulse_unref();
if (data->device)
@@ -332,7 +400,37 @@ static void pulse_destroy(void *vptr)
blog(LOG_DEBUG, "pulse-input: Input destroyed");
}
-/*
+/**
+ * Update the input settings
+ */
+static void pulse_update(void *vptr, obs_data_t settings)
+{
+ PULSE_DATA(vptr);
+ bool restart = false;
+ char *new_device;
+
+ new_device = bstrdup(obs_data_getstring(settings, "device_id"));
+ if (!data->device || strcmp(data->device, new_device) != 0) {
+ if (data->device)
+ bfree(data->device);
+ data->device = new_device;
+ restart = true;
+ }
+
+ if (data->ostime != obs_data_getbool(settings, "ostime")) {
+ data->ostime = obs_data_getbool(settings, "ostime");
+ restart = true;
+ }
+
+ if (!restart)
+ return;
+
+ if (data->stream)
+ pulse_stop_recording(data);
+ pulse_start_recording(data);
+}
+
+/**
* Create the plugin object
*/
static void *pulse_create(obs_data_t settings, obs_source_t source)
@@ -341,24 +439,17 @@ static void *pulse_create(obs_data_t settings, obs_source_t source)
data->source = source;
data->speakers = SPEAKERS_STEREO;
- data->device = bstrdup(obs_data_getstring(settings, "device_id"));
pulse_init();
- if (pulse_start_recording(data) < 0)
- goto fail;
+ pulse_update(data, settings);
+
+ if (data->stream)
+ return data;
- return data;
-fail:
pulse_destroy(data);
return NULL;
}
-static void pulse_update(void *vptr, obs_data_t settings)
-{
- UNUSED_PARAMETER(vptr);
- UNUSED_PARAMETER(settings);
-}
-
struct obs_source_info pulse_input_capture = {
.id = "pulse_input_capture",
.type = OBS_SOURCE_TYPE_INPUT,
@@ -367,7 +458,7 @@ struct obs_source_info pulse_input_capture = {
.create = pulse_create,
.destroy = pulse_destroy,
.update = pulse_update,
- .defaults = pulse_defaults,
+ .defaults = pulse_input_defaults,
.properties = pulse_input_properties
};
@@ -379,6 +470,6 @@ struct obs_source_info pulse_output_capture = {
.create = pulse_create,
.destroy = pulse_destroy,
.update = pulse_update,
- .defaults = pulse_defaults,
+ .defaults = pulse_output_defaults,
.properties = pulse_output_properties
};
diff --git a/plugins/linux-pulseaudio/pulse-wrapper.c b/plugins/linux-pulseaudio/pulse-wrapper.c
index a491c1879453f52301d4c95ee3d1a943f8846369..2f33709f7bd866bbcf382856e7815d7661561497 100644
--- a/plugins/linux-pulseaudio/pulse-wrapper.c
+++ b/plugins/linux-pulseaudio/pulse-wrapper.c
@@ -86,7 +86,7 @@ static void pulse_init_context()
static int_fast32_t pulse_context_ready()
{
pulse_lock();
-
+
if (!PA_CONTEXT_IS_GOOD(pa_context_get_state(pulse_context))) {
pulse_unlock();
return -1;
diff --git a/plugins/linux-pulseaudio/pulse-wrapper.h b/plugins/linux-pulseaudio/pulse-wrapper.h
index e9db5a5da4e8a7353ea2347559eedc68f206e3fc..493fc3de8e567bd92769a030b41dc6cf4bb1db8b 100644
--- a/plugins/linux-pulseaudio/pulse-wrapper.h
+++ b/plugins/linux-pulseaudio/pulse-wrapper.h
@@ -83,7 +83,7 @@ void pulse_accept();
* Request source information
*
* The function will block until the operation was executed and the mainloop
- * called the provided callback functions.
+ * called the provided callback function.
*
* @return negative on error
*
@@ -97,7 +97,7 @@ int_fast32_t pulse_get_source_info_list(pa_source_info_cb_t cb, void *userdata);
* Request server information
*
* The function will block until the operation was executed and the mainloop
- * called the provided callback functions
+ * called the provided callback function.
*
* @return negative on error
*