int_fast32_t pulse_get_server_info(pa_server_info_cb_t cb, void* userdata) { if (pulse_context_ready() < 0) return -1; pulse_lock(); pa_operation *op = pa_context_get_server_info( pulse_context, cb, userdata); while (pa_operation_get_state(op) == PA_OPERATION_RUNNING) pulse_wait(); pa_operation_unref(op); pulse_unlock(); return 0; }
/** * Initialize the pulse audio context with properties and callback */ static void pulse_init_context() { pulse_lock(); pa_proplist *p = pulse_properties(); pulse_context = pa_context_new_with_proplist( pa_threaded_mainloop_get_api(pulse_mainloop), "OBS", p); pa_context_set_state_callback(pulse_context, pulse_context_state_changed, NULL); pa_context_connect(pulse_context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL); pa_proplist_free(p); pulse_unlock(); }
pa_stream* pulse_stream_new(const char* name, const pa_sample_spec* ss, const pa_channel_map* map) { if (pulse_context_ready() < 0) return NULL; pulse_lock(); pa_proplist *p = pulse_properties(); pa_stream *s = pa_stream_new_with_proplist( pulse_context, name, ss, map, p); pa_proplist_free(p); pulse_unlock(); return s; }
/** * stop recording */ static void pulse_stop_recording(struct pulse_data *data) { if (data->stream) { pulse_lock(); pa_stream_disconnect(data->stream); pa_stream_unref(data->stream); data->stream = NULL; pulse_unlock(); } blog(LOG_INFO, "Stopped recording from '%s'", data->device); blog(LOG_INFO, "Got %"PRIuFAST32" packets with %"PRIuFAST64" frames", data->packets, data->frames); data->first_ts = 0; data->packets = 0; data->frames = 0; }
void pulse_unref() { pthread_mutex_lock(&pulse_mutex); if (--pulse_refs == 0) { pulse_lock(); if (pulse_context != NULL) { pa_context_disconnect(pulse_context); pa_context_unref(pulse_context); pulse_context = NULL; } pulse_unlock(); if (pulse_mainloop != NULL) { pa_threaded_mainloop_stop(pulse_mainloop); pa_threaded_mainloop_free(pulse_mainloop); pulse_mainloop = NULL; } } pthread_mutex_unlock(&pulse_mutex); }
/** * Start recording * * We request the default format used by pulse here because the data will be * converted and possibly re-sampled by obs anyway. * * For now we request a buffer length of 25ms although pulse seems to ignore * this setting for monitor streams. For "real" input streams this should work * fine though. */ 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, "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; } if (data->format == PA_SAMPLE_INVALID) { blog(LOG_ERROR, "An error occurred while getting the source info!"); return -1; } pa_sample_spec spec; spec.format = data->format; spec.rate = data->samples_per_sec; spec.channels = data->channels; if (!pa_sample_spec_valid(&spec)) { blog(LOG_ERROR, "Sample spec is not valid"); return -1; } data->speakers = pulse_channels_to_obs_speakers(spec.channels); data->bytes_per_frame = pa_frame_size(&spec); data->stream = pulse_stream_new(obs_source_get_name(data->source), &spec, NULL); if (!data->stream) { blog(LOG_ERROR, "Unable to create stream"); return -1; } pulse_lock(); pa_stream_set_read_callback(data->stream, pulse_stream_read, (void *) data); pulse_unlock(); pa_buffer_attr attr; attr.fragsize = pa_usec_to_bytes(25000, &spec); attr.maxlength = (uint32_t) -1; attr.minreq = (uint32_t) -1; attr.prebuf = (uint32_t) -1; attr.tlength = (uint32_t) -1; pa_stream_flags_t flags = PA_STREAM_ADJUST_LATENCY; pulse_lock(); int_fast32_t ret = pa_stream_connect_record(data->stream, data->device, &attr, flags); pulse_unlock(); if (ret < 0) { pulse_stop_recording(data); blog(LOG_ERROR, "Unable to connect to stream"); return -1; } blog(LOG_INFO, "Started recording from '%s'", data->device); return 0; }