linux-pulseaudio: Use interpolated system timing
Before it was giving timestamps based upon system time for each new segment of audio data. Also, it was subtracting pulse latency from the audio timestamp, which seems like it was really meant for use with the pulse audio time rather than system time. Now, it just uses system time for timestamps. Still might not be totally perfect, but seems to be much better than it was. This also removes the latency calculation. Latency is no longer used because we're not using pulseaudio timing.master
parent
164f9b4517
commit
4e308c955d
|
@ -21,6 +21,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
|
||||
#include "pulse-wrapper.h"
|
||||
|
||||
#define NSEC_PER_SEC 1000000000LL
|
||||
#define NSEC_PER_MSEC 1000000L
|
||||
|
||||
#define PULSE_DATA(voidptr) struct pulse_data *data = voidptr;
|
||||
#define blog(level, msg, ...) blog(level, "pulse-input: " msg, ##__VA_ARGS__)
|
||||
|
||||
|
@ -37,11 +40,11 @@ struct pulse_data {
|
|||
uint_fast32_t samples_per_sec;
|
||||
uint_fast32_t bytes_per_frame;
|
||||
uint_fast8_t channels;
|
||||
uint64_t first_ts;
|
||||
|
||||
/* statistics */
|
||||
uint_fast32_t packets;
|
||||
uint_fast64_t frames;
|
||||
double latency;
|
||||
};
|
||||
|
||||
static void pulse_stop_recording(struct pulse_data *data);
|
||||
|
@ -89,20 +92,18 @@ static enum speaker_layout pulse_channels_to_obs_speakers(
|
|||
return SPEAKERS_UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get latency for a pulse audio stream
|
||||
*/
|
||||
static inline int pulse_get_stream_latency(pa_stream *stream, int64_t *latency)
|
||||
static inline uint64_t samples_to_ns(size_t frames, uint_fast32_t rate)
|
||||
{
|
||||
int ret;
|
||||
int sign;
|
||||
pa_usec_t abs;
|
||||
|
||||
ret = pa_stream_get_latency(stream, &abs, &sign);
|
||||
*latency = (sign) ? -(int64_t) abs : (int64_t) abs;
|
||||
return ret;
|
||||
return frames * NSEC_PER_SEC / rate;
|
||||
}
|
||||
|
||||
static inline uint64_t get_sample_time(size_t frames, uint_fast32_t rate)
|
||||
{
|
||||
return os_gettime_ns() - samples_to_ns(frames, rate);
|
||||
}
|
||||
|
||||
#define STARTUP_TIMEOUT_NS (500 * NSEC_PER_MSEC)
|
||||
|
||||
/**
|
||||
* Callback for pulse which gets executed when new audio data is available
|
||||
*
|
||||
|
@ -116,7 +117,6 @@ static void pulse_stream_read(pa_stream *p, size_t nbytes, void *userdata)
|
|||
|
||||
const void *frames;
|
||||
size_t bytes;
|
||||
int64_t latency;
|
||||
|
||||
if (!data->stream)
|
||||
goto exit;
|
||||
|
@ -134,24 +134,23 @@ static void pulse_stream_read(pa_stream *p, size_t nbytes, void *userdata)
|
|||
goto exit;
|
||||
}
|
||||
|
||||
if (pulse_get_stream_latency(data->stream, &latency) < 0) {
|
||||
blog(LOG_ERROR, "Failed to get timing info !");
|
||||
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 = os_gettime_ns() - (latency * 1000ULL);
|
||||
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;
|
||||
data->latency += latency;
|
||||
|
||||
pa_stream_drop(data->stream);
|
||||
exit:
|
||||
|
@ -203,8 +202,6 @@ skip:
|
|||
*
|
||||
* We request the default format used by pulse here because the data will be
|
||||
* converted and possibly re-sampled by obs anyway.
|
||||
*
|
||||
* The targeted latency for recording is 25ms.
|
||||
*/
|
||||
static int_fast32_t pulse_start_recording(struct pulse_data *data)
|
||||
{
|
||||
|
@ -248,10 +245,7 @@ static int_fast32_t pulse_start_recording(struct pulse_data *data)
|
|||
attr.fragsize = 25000;
|
||||
attr.maxlength = (uint32_t) -1;
|
||||
|
||||
pa_stream_flags_t flags =
|
||||
PA_STREAM_INTERPOLATE_TIMING
|
||||
| PA_STREAM_AUTO_TIMING_UPDATE
|
||||
| PA_STREAM_ADJUST_LATENCY;
|
||||
pa_stream_flags_t flags = PA_STREAM_ADJUST_LATENCY;
|
||||
|
||||
pulse_lock();
|
||||
int_fast32_t ret = pa_stream_connect_record(data->stream, data->device,
|
||||
|
@ -280,16 +274,13 @@ static void pulse_stop_recording(struct pulse_data *data)
|
|||
pulse_unlock();
|
||||
}
|
||||
|
||||
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->first_ts = 0;
|
||||
data->packets = 0;
|
||||
data->frames = 0;
|
||||
data->latency = 0.0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue