/** * context status change callback * * @todo this is currently a noop, we want to reconnect here if the connection * is lost ... */ static void pulse_context_state_changed(pa_context *c, void *userdata) { UNUSED_PARAMETER(userdata); UNUSED_PARAMETER(c); pulse_signal(0); }
/** * Server info callback */ static void pulse_server_info(pa_context *c, const pa_server_info *i, void *userdata) { UNUSED_PARAMETER(c); PULSE_DATA(userdata); blog(LOG_INFO, "Server name: '%s %s'", i->server_name, i->server_version); if (data->device && strcmp("default", data->device) == 0) { if (data->input) { bfree(data->device); data->device = bstrdup(i->default_source_name); blog(LOG_DEBUG, "Default input device: '%s'", data->device); } else { char *monitor = bzalloc(strlen(i->default_sink_name) + 9); strcat(monitor, i->default_sink_name); strcat(monitor, ".monitor"); bfree(data->device); data->device = bstrdup(monitor); blog(LOG_DEBUG, "Default output device: '%s'", data->device); bfree(monitor); } } pulse_signal(0); }
/** * Server info callback */ static void pulse_server_info(pa_context *c, const pa_server_info *i, void *userdata) { UNUSED_PARAMETER(c); UNUSED_PARAMETER(userdata); blog(LOG_INFO, "Server name: '%s %s'", i->server_name, i->server_version); pulse_signal(0); }
/** * Server info callback */ static void pulse_input_device(pa_context *c, const pa_server_info *i, void *userdata) { 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, "Default input device: '%s'", i->default_source_name); pulse_signal(0); }
/** * output info callback */ static void pulse_output_info(pa_context *c, const pa_source_info *i, int eol, void *userdata) { UNUSED_PARAMETER(c); if (eol != 0 || i->monitor_of_sink == PA_INVALID_INDEX) goto skip; obs_property_list_add_string((obs_property_t*) userdata, i->description, i->name); skip: pulse_signal(0); }
/** * 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) { UNUSED_PARAMETER(p); UNUSED_PARAMETER(nbytes); PULSE_DATA(userdata); const void *frames; size_t bytes; int64_t latency; if (!data->stream) goto exit; pa_stream_peek(data->stream, &frames, &bytes); // check if we got data if (!bytes) goto exit; if (!frames) { blog(LOG_ERROR, "pulse-input: 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 !"); pa_stream_drop(data->stream); goto exit; } struct source_audio out; out.speakers = data->speakers; out.samples_per_sec = data->samples_per_sec; out.format = pulse_to_obs_audio_format(data->format); out.data[0] = (uint8_t *) frames; out.frames = bytes / data->bytes_per_frame; out.timestamp = os_gettime_ns() - (latency * 1000ULL); obs_source_output_audio(data->source, &out); data->packets++; data->frames += out.frames; pa_stream_drop(data->stream); exit: 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, "Default output device: '%s'", monitor); bfree(monitor); pulse_signal(0); }
/** * 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) { UNUSED_PARAMETER(p); UNUSED_PARAMETER(nbytes); PULSE_DATA(userdata); const void *frames; size_t bytes; if (!data->stream) goto exit; pa_stream_peek(data->stream, &frames, &bytes); // check if we got data if (!bytes) goto exit; if (!frames) { blog(LOG_ERROR, "Got audio hole of %u bytes", (unsigned int) bytes); pa_stream_drop(data->stream); goto exit; } struct obs_source_audio out; out.speakers = data->speakers; out.samples_per_sec = data->samples_per_sec; out.format = pulse_to_obs_audio_format(data->format); out.data[0] = (uint8_t *) frames; out.frames = bytes / data->bytes_per_frame; out.timestamp = get_sample_time(out.frames, out.samples_per_sec); if (!data->first_ts) data->first_ts = out.timestamp + STARTUP_TIMEOUT_NS; if (out.timestamp > data->first_ts) obs_source_output_audio(data->source, &out); data->packets++; data->frames += out.frames; pa_stream_drop(data->stream); exit: pulse_signal(0); }
/** * Source info callback * * We use the default stream settings for recording here unless pulse is * configured to something obs can't deal with. */ static void pulse_source_info(pa_context *c, const pa_source_info *i, int eol, void *userdata) { UNUSED_PARAMETER(c); PULSE_DATA(userdata); // An error occured if (eol < 0) { data->format = PA_SAMPLE_INVALID; goto skip; } // Terminating call for multi instance callbacks if (eol > 0) goto skip; blog(LOG_INFO, "Audio format: %s, %"PRIu32" Hz" ", %"PRIu8" channels", pa_sample_format_to_string(i->sample_spec.format), i->sample_spec.rate, i->sample_spec.channels); pa_sample_format_t format = i->sample_spec.format; if (pulse_to_obs_audio_format(format) == AUDIO_FORMAT_UNKNOWN) { format = PA_SAMPLE_S16LE; blog(LOG_INFO, "Sample format %s not supported by OBS," "using %s instead for recording", pa_sample_format_to_string(i->sample_spec.format), pa_sample_format_to_string(format)); } uint8_t channels = i->sample_spec.channels; if (pulse_channels_to_obs_speakers(channels) == SPEAKERS_UNKNOWN) { channels = 2; blog(LOG_INFO, "%c channels not supported by OBS," "using %c instead for recording", i->sample_spec.channels, channels); } data->format = format; data->samples_per_sec = i->sample_spec.rate; data->channels = channels; skip: pulse_signal(0); }
/* * Server info callback */ static void pulse_server_info(pa_context *c, const pa_server_info *i, void *userdata) { UNUSED_PARAMETER(c); PULSE_DATA(userdata); data->format = i->sample_spec.format; data->samples_per_sec = i->sample_spec.rate; data->channels = i->sample_spec.channels; blog(LOG_DEBUG, "pulse-input: Default format: %s, %u Hz, %u channels", pa_sample_format_to_string(i->sample_spec.format), i->sample_spec.rate, i->sample_spec.channels); pulse_signal(0); }
/** * Server info callback */ static void pulse_server_info(pa_context *c, const pa_server_info *i, void *userdata) { UNUSED_PARAMETER(c); PULSE_DATA(userdata); blog(LOG_INFO, "pulse-input: Server name: '%s %s'", i->server_name, i->server_version); 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); pulse_signal(0); }