/** * 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; } 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; }
/** * 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); }