Merge pull request #261 from fryshorts/pulse-input

Use default source settings for recording in pulse input
master
Jim 2014-09-12 13:03:54 -07:00
commit 07c2d32a68
3 changed files with 97 additions and 39 deletions

View File

@ -22,6 +22,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#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;
@ -40,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);
@ -51,16 +53,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;
@ -80,13 +77,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;
@ -131,14 +128,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;
}
@ -154,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:
@ -167,21 +165,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'",
blog(LOG_INFO, "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);
blog(LOG_INFO, "Audio format: %s, %"PRIuFAST32" Hz"
", %"PRIuFAST8" channels",
pa_sample_format_to_string(data->format),
data->samples_per_sec,
data->channels);
skip:
pulse_signal(0);
}
@ -196,7 +209,13 @@ static void pulse_server_info(pa_context *c, const pa_server_info *i,
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, "Unable to get source info !");
return -1;
}
@ -206,7 +225,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;
}
@ -216,7 +235,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;
}
@ -240,12 +259,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;
}
@ -262,13 +280,16 @@ 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",
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;
}
/**
@ -344,8 +365,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);
}
@ -361,7 +381,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);

View File

@ -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)

View File

@ -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
*