From f9e2058820c2a51994708ad11d1ed8e09b12b8b1 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 16 Sep 2004 00:05:56 +0000 Subject: add input latency measurement add GETOSPACE support to module-oss git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@205 fefdeb5f-60dc-0310-8127-8f9354f1896f --- doc/todo | 4 +- polyp/cli-text.c | 3 +- polyp/iochannel.c | 11 ++++ polyp/iochannel.h | 3 + polyp/module-alsa-source.c | 15 +++++ polyp/module-oss.c | 141 ++++++++++++++++++++++++++++++++------------ polyp/native-common.h | 1 + polyp/pacat.c | 18 +++--- polyp/polyplib-def.h | 28 +++++---- polyp/polyplib-introspect.c | 8 ++- polyp/polyplib-introspect.h | 5 +- polyp/polyplib-stream.c | 11 +++- polyp/protocol-esound.c | 9 ++- polyp/protocol-native.c | 62 ++++++++++++++++++- polyp/protocol-simple.c | 7 +++ polyp/sink.c | 5 +- polyp/source-output.c | 11 ++++ polyp/source-output.h | 4 ++ polyp/source.c | 11 ++++ polyp/source.h | 3 + 20 files changed, 292 insertions(+), 68 deletions(-) diff --git a/doc/todo b/doc/todo index 22fa4421..675e8ca3 100644 --- a/doc/todo +++ b/doc/todo @@ -2,7 +2,6 @@ *** 0.5 *** - more complete pactl -- input latency - fix tcp/native - paman: add support for killing sink inputs, source outputs, clients - add client config file @@ -15,6 +14,7 @@ - udp based protocol - make mcalign merge chunks - option to use default fragment size on alsa drivers +- improve module-oss-mmap latency measurement ** later *** - xmlrpc/http @@ -27,7 +27,7 @@ backends for: - portaudio +- alsa-lib - sdl - gstreamer (semi-done) -- alsa-lib - OSS (esddsp style) diff --git a/polyp/cli-text.c b/polyp/cli-text.c index 0915be8b..9932e568 100644 --- a/polyp/cli-text.c +++ b/polyp/cli-text.c @@ -125,10 +125,11 @@ char *pa_source_list_to_string(struct pa_core *c) { for (source = pa_idxset_first(c->sources, &index); source; source = pa_idxset_next(c->sources, &index)) { char ss[PA_SAMPLE_SNPRINT_MAX_LENGTH]; pa_sample_spec_snprint(ss, sizeof(ss), &source->sample_spec); - pa_strbuf_printf(s, " %c index: %u\n\tname: <%s>\n\tsample_spec: <%s>\n", + pa_strbuf_printf(s, " %c index: %u\n\tname: <%s>\n\tlatency: <%0.0f usec>\n\tsample_spec: <%s>\n", c->default_source_name && !strcmp(source->name, c->default_source_name) ? '*' : ' ', source->index, source->name, + (float) pa_source_get_latency(source), ss); if (source->monitor_of) diff --git a/polyp/iochannel.c b/polyp/iochannel.c index 1aa70b93..72bdac20 100644 --- a/polyp/iochannel.c +++ b/polyp/iochannel.c @@ -229,3 +229,14 @@ int pa_iochannel_socket_set_sndbuf(struct pa_iochannel *io, size_t l) { assert(io); return pa_socket_set_sndbuf(io->ofd, l); } + +void pa_iochannel_force_unreadable(struct pa_iochannel *io) { + assert(io); + io->readable = 0; + enable_mainloop_sources(io); +} + +void pa_iochannel_force_unwritable(struct pa_iochannel *io) { + io->writable = 0; + enable_mainloop_sources(io); +} diff --git a/polyp/iochannel.h b/polyp/iochannel.h index 6f5f351c..a4edbfad 100644 --- a/polyp/iochannel.h +++ b/polyp/iochannel.h @@ -39,6 +39,9 @@ int pa_iochannel_is_readable(struct pa_iochannel*io); int pa_iochannel_is_writable(struct pa_iochannel*io); int pa_iochannel_is_hungup(struct pa_iochannel*io); +void pa_iochannel_force_unreadable(struct pa_iochannel *io); +void pa_iochannel_force_unwritable(struct pa_iochannel *io); + void pa_iochannel_set_noclose(struct pa_iochannel*io, int b); void pa_iochannel_set_callback(struct pa_iochannel*io, void (*callback)(struct pa_iochannel*io, void *userdata), void *userdata); diff --git a/polyp/module-alsa-source.c b/polyp/module-alsa-source.c index cf828eb0..41a17691 100644 --- a/polyp/module-alsa-source.c +++ b/polyp/module-alsa-source.c @@ -143,6 +143,20 @@ static void io_callback(struct pa_mainloop_api*a, struct pa_io_event *e, int fd, do_read(u); } +static pa_usec_t source_get_latency_cb(struct pa_source *s) { + struct userdata *u = s->userdata; + snd_pcm_sframes_t frames; + assert(s && u && u->source); + + if (snd_pcm_delay(u->pcm_handle, &frames) < 0) { + pa_log(__FILE__": failed to get delay\n"); + s->get_latency = NULL; + return 0; + } + + return pa_bytes_to_usec(frames * u->frame_size, &s->sample_spec); +} + int pa__init(struct pa_core *c, struct pa_module*m) { struct pa_modargs *ma = NULL; int ret = -1; @@ -191,6 +205,7 @@ int pa__init(struct pa_core *c, struct pa_module*m) { assert(u->source); u->source->userdata = u; + u->source->get_latency = source_get_latency_cb; pa_source_set_owner(u->source, m); u->source->description = pa_sprintf_malloc("Advanced Linux Sound Architecture PCM on '%s'", dev); diff --git a/polyp/module-oss.c b/polyp/module-oss.c index a45f72b8..68918604 100644 --- a/polyp/module-oss.c +++ b/polyp/module-oss.c @@ -59,6 +59,7 @@ struct userdata { struct pa_memchunk memchunk, silence; uint32_t in_fragment_size, out_fragment_size, sample_size; + int use_getospace, use_getispace; int fd; struct pa_module *module; @@ -92,6 +93,9 @@ static void update_usage(struct userdata *u) { static void do_write(struct userdata *u) { struct pa_memchunk *memchunk; ssize_t r; + size_t l; + int loop = 0; + assert(u); if (!u->sink || !pa_iochannel_is_writable(u->io)) @@ -99,37 +103,58 @@ static void do_write(struct userdata *u) { update_usage(u); - memchunk = &u->memchunk; - - if (!memchunk->length) - if (pa_sink_render(u->sink, u->out_fragment_size, memchunk) < 0) - memchunk = &u->silence; - - assert(memchunk->memblock); - assert(memchunk->memblock->data); - assert(memchunk->length); + l = u->out_fragment_size; - if ((r = pa_iochannel_write(u->io, (uint8_t*) memchunk->memblock->data + memchunk->index, memchunk->length)) < 0) { - pa_log(__FILE__": write() failed: %s\n", strerror(errno)); - return; + if (u->use_getospace) { + audio_buf_info info; + + if (ioctl(u->fd, SNDCTL_DSP_GETOSPACE, &info) < 0) + u->use_getospace = 0; + else { + if (info.bytes/l > 0) { + l = (info.bytes/l)*l; + loop = 1; + } + } } + + do { + memchunk = &u->memchunk; + + if (!memchunk->length) + if (pa_sink_render(u->sink, l, memchunk) < 0) + memchunk = &u->silence; - if (memchunk == &u->silence) - assert(r % u->sample_size == 0); - else { - u->memchunk.index += r; - u->memchunk.length -= r; + assert(memchunk->memblock); + assert(memchunk->memblock->data); + assert(memchunk->length); - if (u->memchunk.length <= 0) { - pa_memblock_unref(u->memchunk.memblock); - u->memchunk.memblock = NULL; + if ((r = pa_iochannel_write(u->io, (uint8_t*) memchunk->memblock->data + memchunk->index, memchunk->length)) < 0) { + pa_log(__FILE__": write() failed: %s\n", strerror(errno)); + break; } - } + + if (memchunk == &u->silence) + assert(r % u->sample_size == 0); + else { + u->memchunk.index += r; + u->memchunk.length -= r; + + if (u->memchunk.length <= 0) { + pa_memblock_unref(u->memchunk.memblock); + u->memchunk.memblock = NULL; + } + } + + l = l > (size_t) r ? l - r : 0; + } while (loop && l > 0); } static void do_read(struct userdata *u) { struct pa_memchunk memchunk; ssize_t r; + size_t l; + int loop = 0; assert(u); if (!u->source || !pa_iochannel_is_readable(u->io)) @@ -137,21 +162,40 @@ static void do_read(struct userdata *u) { update_usage(u); - memchunk.memblock = pa_memblock_new(u->in_fragment_size, u->core->memblock_stat); - assert(memchunk.memblock); - if ((r = pa_iochannel_read(u->io, memchunk.memblock->data, memchunk.memblock->length)) < 0) { - pa_memblock_unref(memchunk.memblock); - if (errno != EAGAIN) - pa_log(__FILE__": read() failed: %s\n", strerror(errno)); - return; - } + l = u->in_fragment_size; - assert(r <= (ssize_t) memchunk.memblock->length); - memchunk.length = memchunk.memblock->length = r; - memchunk.index = 0; + if (u->use_getispace) { + audio_buf_info info; + + if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) < 0) + u->use_getispace = 0; + else { + if (info.bytes/l > 0) { + l = (info.bytes/l)*l; + loop = 1; + } + } + } + + do { + memchunk.memblock = pa_memblock_new(l, u->core->memblock_stat); + assert(memchunk.memblock); + if ((r = pa_iochannel_read(u->io, memchunk.memblock->data, memchunk.memblock->length)) < 0) { + pa_memblock_unref(memchunk.memblock); + if (errno != EAGAIN) + pa_log(__FILE__": read() failed: %s\n", strerror(errno)); + break; + } + + assert(r <= (ssize_t) memchunk.memblock->length); + memchunk.length = memchunk.memblock->length = r; + memchunk.index = 0; + + pa_source_post(u->source, &memchunk); + pa_memblock_unref(memchunk.memblock); - pa_source_post(u->source, &memchunk); - pa_memblock_unref(memchunk.memblock); + l = l > (size_t) r ? l - r : 0; + } while (loop && l > 0); } static void io_callback(struct pa_iochannel *io, void*userdata) { @@ -181,6 +225,25 @@ static pa_usec_t sink_get_latency_cb(struct pa_sink *s) { return r; } +static pa_usec_t source_get_latency_cb(struct pa_source *s) { + struct userdata *u = s->userdata; + audio_buf_info info; + assert(s && u && u->sink); + + if (!u->use_getispace) + return 0; + + if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) < 0) { + u->use_getispace = 0; + return 0; + } + + if (info.bytes <= 0) + return 0; + + return pa_bytes_to_usec(info.bytes, &s->sample_spec); +} + int pa__init(struct pa_core *c, struct pa_module*m) { struct audio_buf_info info; struct userdata *u = NULL; @@ -243,23 +306,27 @@ int pa__init(struct pa_core *c, struct pa_module*m) { assert(frag_size); in_frag_size = out_frag_size = frag_size; + u = pa_xmalloc(sizeof(struct userdata)); + u->core = c; + u->use_getospace = u->use_getispace = 0; + if (ioctl(fd, SNDCTL_DSP_GETISPACE, &info) >= 0) { pa_log(__FILE__": input -- %u fragments of size %u.\n", info.fragstotal, info.fragsize); in_frag_size = info.fragsize; + u->use_getispace = 1; } if (ioctl(fd, SNDCTL_DSP_GETOSPACE, &info) >= 0) { pa_log(__FILE__": output -- %u fragments of size %u.\n", info.fragstotal, info.fragsize); out_frag_size = info.fragsize; + u->use_getospace = 1; } - u = pa_xmalloc(sizeof(struct userdata)); - u->core = c; - if (mode != O_WRONLY) { u->source = pa_source_new(c, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss); assert(u->source); u->source->userdata = u; + u->source->get_latency = source_get_latency_cb; pa_source_set_owner(u->source, m); u->source->description = pa_sprintf_malloc("Open Sound System PCM on '%s'", p); } else diff --git a/polyp/native-common.h b/polyp/native-common.h index 45e0b1d3..a052fca2 100644 --- a/polyp/native-common.h +++ b/polyp/native-common.h @@ -86,6 +86,7 @@ enum { PA_COMMAND_REMOVE_AUTOLOAD, PA_COMMAND_GET_AUTOLOAD_INFO, PA_COMMAND_GET_AUTOLOAD_INFO_LIST, + PA_COMMAND_GET_RECORD_LATENCY, PA_COMMAND_MAX }; diff --git a/polyp/pacat.c b/polyp/pacat.c index cc7d55f4..933b0c3a 100644 --- a/polyp/pacat.c +++ b/polyp/pacat.c @@ -276,27 +276,29 @@ static void exit_signal_callback(struct pa_mainloop_api*m, struct pa_signal_even } -/* Show the current playback latency */ +/* Show the current latency */ static void stream_get_latency_callback(struct pa_stream *s, const struct pa_latency_info *i, void *userdata) { + double total; assert(s); if (!i) { - fprintf(stderr, "Failed to get latency: %s\n", strerror(errno)); + fprintf(stderr, "Failed to get latency: %s\n", pa_strerror(pa_context_errno(context))); quit(1); return; } - fprintf(stderr, "Latency: buffer: %0.0f usec; sink: %0.0f usec; transport: %0.0f usec; total: %0.0f usec; synchronized clocks: %s.\n", - (float) i->buffer_usec, (float) i->sink_usec, (float) i->transport_usec, - (float) (i->buffer_usec+i->sink_usec+i->transport_usec), + if (mode == PLAYBACK) + total = (double) i->sink_usec + i->buffer_usec + i->transport_usec; + else + total = (double) i->source_usec + i->buffer_usec + i->transport_usec - i->sink_usec; + + fprintf(stderr, "Latency: buffer: %0.0f usec; sink: %0.0f usec; source: %0.0f usec; transport: %0.0f usec; total: %0.0f usec; synchronized clocks: %s.\n", + (float) i->buffer_usec, (float) i->sink_usec, (float) i->source_usec, (float) i->transport_usec, total, i->synchronized_clocks ? "yes" : "no"); } /* Someone requested that the latency is shown */ static void sigusr1_signal_callback(struct pa_mainloop_api*m, struct pa_signal_event *e, int sig, void *userdata) { - if (mode != PLAYBACK) - return; - fprintf(stderr, "Got SIGUSR1, requesting latency.\n"); pa_operation_unref(pa_stream_get_latency(stream, stream_get_latency_callback, NULL)); } diff --git a/polyp/polyplib-def.h b/polyp/polyplib-def.h index 9bba3f32..29f5eb43 100644 --- a/polyp/polyplib-def.h +++ b/polyp/polyplib-def.h @@ -132,20 +132,26 @@ enum pa_subscription_event_type { /** Return one if an event type t matches an event mask bitfield */ #define pa_subscription_match_flags(m, t) (!!((m) & (1 << ((t) & PA_SUBSCRIPTION_EVENT_FACILITY_MASK)))) -/** A structure for latency info. See pa_stream_get_latency(). The - * total latency a sample that is written with pa_stream_write() takes - * to be played may be estimated by - * buffer_usec+sink_usec+transport_usec. The buffer to which +/** A structure for latency info. See pa_stream_get_latency(). The + * total output latency a sample that is written with + * pa_stream_write() takes to be played may be estimated by + * sink_usec+buffer_usec+transport_usec. The output buffer to which * buffer_usec relates may be manipulated freely (with * pa_stream_write()'s delta argument, pa_stream_flush() and friends), - * the playback buffer sink_usec relates to is a FIFO which cannot be - * flushed or manipulated in any way. */ + * the buffers sink_usec/source_usec relates to is a first-in + * first-out buffer which cannot be flushed or manipulated in any + * way. The total input latency a sample that is recorded takes to be + * delivered to the application is: + * source_usec+buffer_usec+transport_usec-sink_usec. (Take care of + * sign issues!) When connected to a monitor source sink_usec contains + * the latency of the owning sink.*/ struct pa_latency_info { - pa_usec_t buffer_usec; /**< Time in usecs the current buffer takes to play */ - pa_usec_t sink_usec; /**< Time in usecs a sample takes to be played on the sink. */ - pa_usec_t transport_usec; /**< Estimated time in usecs a sample takes to be transferred to the daemon. \since 0.5 */ - int playing; /**< Non-zero when the stream is currently playing */ - uint32_t queue_length; /**< Queue size in bytes. */ + pa_usec_t buffer_usec; /**< Time in usecs the current buffer takes to play. For both playback and record streams. */ + pa_usec_t sink_usec; /**< Time in usecs a sample takes to be played on the sink. For playback streams and record streams connected to a monitor source. */ + pa_usec_t source_usec; /**< Time in usecs a sample takes from being recorded to being delivered to the application. Only for record streams. \since 0.5*/ + pa_usec_t transport_usec; /**< Estimated time in usecs a sample takes to be transferred to/from the daemon. For both playback and record streams. \since 0.5 */ + int playing; /**< Non-zero when the stream is currently playing. Only for playback streams. */ + uint32_t queue_length; /**< Queue size in bytes. For both playback and recrd streams. */ int synchronized_clocks; /**< Non-zero if the local and the * remote machine have synchronized * clocks. If synchronized clocks are diff --git a/polyp/polyplib-introspect.c b/polyp/polyplib-introspect.c index 919adb9c..1673be9b 100644 --- a/polyp/polyplib-introspect.c +++ b/polyp/polyplib-introspect.c @@ -221,7 +221,9 @@ static void context_get_source_info_callback(struct pa_pdispatch *pd, uint32_t c pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 || pa_tagstruct_getu32(t, &i.owner_module) < 0 || pa_tagstruct_getu32(t, &i.monitor_of_sink) < 0 || - pa_tagstruct_gets(t, &i.monitor_of_sink_name) < 0) { + pa_tagstruct_gets(t, &i.monitor_of_sink_name) < 0 || + pa_tagstruct_get_usec(t, &i.latency) < 0) { + pa_context_fail(o->context, PA_ERROR_PROTOCOL); goto finish; } @@ -515,7 +517,9 @@ static void context_get_source_output_info_callback(struct pa_pdispatch *pd, uin pa_tagstruct_getu32(t, &i.owner_module) < 0 || pa_tagstruct_getu32(t, &i.client) < 0 || pa_tagstruct_getu32(t, &i.source) < 0 || - pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0) { + pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 || + pa_tagstruct_get_usec(t, &i.buffer_usec) < 0 || + pa_tagstruct_get_usec(t, &i.source_usec) < 0) { pa_context_fail(o->context, PA_ERROR_PROTOCOL); goto finish; } diff --git a/polyp/polyplib-introspect.h b/polyp/polyplib-introspect.h index 51210457..28c51fed 100644 --- a/polyp/polyplib-introspect.h +++ b/polyp/polyplib-introspect.h @@ -56,7 +56,7 @@ struct pa_sink_info { pa_volume_t volume; /**< Volume of the sink */ uint32_t monitor_source; /**< Index of the monitor source connected to this sink */ const char *monitor_source_name; /**< The name of the monitor source */ - pa_usec_t latency; /**< Length of the playback buffer of this sink */ + pa_usec_t latency; /**< Length of filled playback buffer of this sink */ }; /** Get information about a sink by its name */ @@ -77,6 +77,7 @@ struct pa_source_info { uint32_t owner_module; /**< Owning module index, or PA_INVALID_INDEX */ uint32_t monitor_of_sink; /**< If this is a monitor source the index of the owning sink, otherwise PA_INVALID_INDEX */ const char *monitor_of_sink_name; /**< Name of the owning sink, or PA_INVALID_INDEX */ + pa_usec_t latency; /**< Length of filled record buffer of this source. \since 0.5 */ }; /** Get information about a source by its name */ @@ -158,6 +159,8 @@ struct pa_source_output_info { uint32_t client; /**< Index of the client this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any client */ uint32_t source; /**< Index of the connected source */ struct pa_sample_spec sample_spec; /**< The sample specification of the source output */ + pa_usec_t buffer_usec; /**< Latency due to buffering in the source output, see pa_latency_info for details. \since 0.5 */ + pa_usec_t source_usec; /**< Latency of the source device, see pa_latency_info for details. \since 0.5 */ }; /** Get information about a source output by its index */ diff --git a/polyp/polyplib-stream.c b/polyp/polyplib-stream.c index 89a8d338..532d1700 100644 --- a/polyp/polyplib-stream.c +++ b/polyp/polyplib-stream.c @@ -336,6 +336,7 @@ static void stream_get_latency_callback(struct pa_pdispatch *pd, uint32_t comman } else if (pa_tagstruct_get_usec(t, &i.buffer_usec) < 0 || pa_tagstruct_get_usec(t, &i.sink_usec) < 0 || + pa_tagstruct_get_usec(t, &i.source_usec) < 0 || pa_tagstruct_get_boolean(t, &i.playing) < 0 || pa_tagstruct_getu32(t, &i.queue_length) < 0 || pa_tagstruct_get_timeval(t, &local) < 0 || @@ -350,7 +351,12 @@ static void stream_get_latency_callback(struct pa_pdispatch *pd, uint32_t comman if (pa_timeval_cmp(&local, &remote) < 0 && pa_timeval_cmp(&remote, &now)) { /* local and remote seem to have synchronized clocks */ - i.transport_usec = pa_timeval_diff(&remote, &local); + + if (o->stream->direction == PA_STREAM_PLAYBACK) + i.transport_usec = pa_timeval_diff(&remote, &local); + else + i.transport_usec = pa_timeval_diff(&now, &remote); + i.synchronized_clocks = 1; i.timestamp = remote; } else { @@ -376,6 +382,7 @@ struct pa_operation* pa_stream_get_latency(struct pa_stream *s, void (*cb)(struc struct pa_operation *o; struct pa_tagstruct *t; struct timeval now; + assert(s && s->direction != PA_STREAM_UPLOAD); o = pa_operation_new(s->context, s); assert(o); @@ -384,7 +391,7 @@ struct pa_operation* pa_stream_get_latency(struct pa_stream *s, void (*cb)(struc t = pa_tagstruct_new(NULL, 0); assert(t); - pa_tagstruct_putu32(t, PA_COMMAND_GET_PLAYBACK_LATENCY); + pa_tagstruct_putu32(t, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_GET_PLAYBACK_LATENCY : PA_COMMAND_GET_RECORD_LATENCY); pa_tagstruct_putu32(t, tag = s->context->ctag++); pa_tagstruct_putu32(t, s->channel); diff --git a/polyp/protocol-esound.c b/polyp/protocol-esound.c index 5abe474d..aff45099 100644 --- a/polyp/protocol-esound.c +++ b/polyp/protocol-esound.c @@ -107,6 +107,7 @@ static void sink_input_drop_cb(struct pa_sink_input *i, const struct pa_memchunk static int sink_input_peek_cb(struct pa_sink_input *i, struct pa_memchunk *chunk); static void sink_input_kill_cb(struct pa_sink_input *i); static pa_usec_t sink_input_get_latency_cb(struct pa_sink_input *i); +static pa_usec_t source_output_get_latency_cb(struct pa_source_output *o); static void source_output_push_cb(struct pa_source_output *o, const struct pa_memchunk *chunk); static void source_output_kill_cb(struct pa_source_output *o); @@ -374,6 +375,7 @@ static int esd_proto_stream_record(struct connection *c, esd_proto_t request, co c->source_output->client = c->client; c->source_output->push = source_output_push_cb; c->source_output->kill = source_output_kill_cb; + c->source_output->get_latency = source_output_get_latency_cb; c->source_output->userdata = c; c->state = ESD_STREAMING_DATA; @@ -919,7 +921,6 @@ static void sink_input_kill_cb(struct pa_sink_input *i) { connection_free((struct connection *) i->userdata); } - static pa_usec_t sink_input_get_latency_cb(struct pa_sink_input *i) { struct connection*c = i->userdata; assert(i && c); @@ -944,6 +945,12 @@ static void source_output_kill_cb(struct pa_source_output *o) { connection_free((struct connection *) o->userdata); } +static pa_usec_t source_output_get_latency_cb(struct pa_source_output *o) { + struct connection*c = o->userdata; + assert(o && c); + return pa_bytes_to_usec(pa_memblockq_get_length(c->output_memblockq), &c->source_output->sample_spec); +} + /*** socket server callback ***/ static void on_connection(struct pa_socket_server*s, struct pa_iochannel *io, void *userdata) { diff --git a/polyp/protocol-native.c b/polyp/protocol-native.c index be7b76f6..e197d1e2 100644 --- a/polyp/protocol-native.c +++ b/polyp/protocol-native.c @@ -117,6 +117,7 @@ static void request_bytes(struct playback_stream*s); static void source_output_kill_cb(struct pa_source_output *o); static void source_output_push_cb(struct pa_source_output *o, const struct pa_memchunk *chunk); +static pa_usec_t source_output_get_latency_cb(struct pa_source_output *o); static void command_exit(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); static void command_create_playback_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); @@ -128,6 +129,7 @@ static void command_set_client_name(struct pa_pdispatch *pd, uint32_t command, u static void command_lookup(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); static void command_stat(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); static void command_get_playback_latency(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); +static void command_get_record_latency(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); static void command_create_upload_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); static void command_finish_upload_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); static void command_play_sample(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); @@ -166,6 +168,7 @@ static const struct pa_pdispatch_command command_table[PA_COMMAND_MAX] = { [PA_COMMAND_LOOKUP_SOURCE] = { command_lookup }, [PA_COMMAND_STAT] = { command_stat }, [PA_COMMAND_GET_PLAYBACK_LATENCY] = { command_get_playback_latency }, + [PA_COMMAND_GET_RECORD_LATENCY] = { command_get_record_latency }, [PA_COMMAND_CREATE_UPLOAD_STREAM] = { command_create_upload_stream }, [PA_COMMAND_DELETE_UPLOAD_STREAM] = { command_delete_stream }, [PA_COMMAND_FINISH_UPLOAD_STREAM] = { command_finish_upload_stream }, @@ -256,6 +259,7 @@ static struct record_stream* record_stream_new(struct connection *c, struct pa_s s->source_output = source_output; s->source_output->push = source_output_push_cb; s->source_output->kill = source_output_kill_cb; + s->source_output->get_latency = source_output_get_latency_cb; s->source_output->userdata = s; s->source_output->owner = c->protocol->module; s->source_output->client = c->client; @@ -444,7 +448,6 @@ static void send_record_stream_killed(struct record_stream *r) { pa_pstream_send_tagstruct(r->connection->pstream, t); } - /*** sinkinput callbacks ***/ static int sink_input_peek_cb(struct pa_sink_input *i, struct pa_memchunk *chunk) { @@ -508,6 +511,16 @@ static void source_output_kill_cb(struct pa_source_output *o) { record_stream_free((struct record_stream *) o->userdata); } +static pa_usec_t source_output_get_latency_cb(struct pa_source_output *o) { + struct record_stream *s; + assert(o && o->userdata); + s = o->userdata; + + /*pa_log(__FILE__": get_latency: %u\n", pa_memblockq_get_length(s->memblockq));*/ + + return pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &o->sample_spec); +} + /*** pdispatch callbacks ***/ static void protocol_error(struct connection *c) { @@ -843,7 +856,7 @@ static void command_get_playback_latency(struct pa_pdispatch *pd, uint32_t comma struct timeval tv, now; uint32_t index; assert(c && t); - + if (pa_tagstruct_getu32(t, &index) < 0 || pa_tagstruct_get_timeval(t, &tv) < 0 || !pa_tagstruct_eof(t)) { @@ -867,6 +880,7 @@ static void command_get_playback_latency(struct pa_pdispatch *pd, uint32_t comma pa_tagstruct_putu32(reply, tag); pa_tagstruct_put_usec(reply, pa_sink_input_get_latency(s->sink_input)); pa_tagstruct_put_usec(reply, pa_sink_get_latency(s->sink_input->sink)); + pa_tagstruct_put_usec(reply, 0); pa_tagstruct_put_boolean(reply, pa_memblockq_is_readable(s->memblockq)); pa_tagstruct_putu32(reply, pa_memblockq_get_length(s->memblockq)); pa_tagstruct_put_timeval(reply, &tv); @@ -875,6 +889,47 @@ static void command_get_playback_latency(struct pa_pdispatch *pd, uint32_t comma pa_pstream_send_tagstruct(c->pstream, reply); } +static void command_get_record_latency(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + struct pa_tagstruct *reply; + struct record_stream *s; + struct timeval tv, now; + uint32_t index; + assert(c && t); + + if (pa_tagstruct_getu32(t, &index) < 0 || + pa_tagstruct_get_timeval(t, &tv) < 0 || + !pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (!c->authorized) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + if (!(s = pa_idxset_get_by_index(c->record_streams, index))) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); + return; + } + + reply = pa_tagstruct_new(NULL, 0); + assert(reply); + pa_tagstruct_putu32(reply, PA_COMMAND_REPLY); + pa_tagstruct_putu32(reply, tag); + pa_tagstruct_put_usec(reply, pa_source_output_get_latency(s->source_output)); + pa_tagstruct_put_usec(reply, s->source_output->source->monitor_of ? pa_sink_get_latency(s->source_output->source->monitor_of) : 0); + pa_tagstruct_put_usec(reply, pa_source_get_latency(s->source_output->source)); + pa_tagstruct_put_boolean(reply, 0); + pa_tagstruct_putu32(reply, pa_memblockq_get_length(s->memblockq)); + pa_tagstruct_put_timeval(reply, &tv); + gettimeofday(&now, NULL); + pa_tagstruct_put_timeval(reply, &now); + pa_pstream_send_tagstruct(c->pstream, reply); +} + + static void command_create_upload_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { struct connection *c = userdata; struct upload_stream *s; @@ -1036,6 +1091,7 @@ static void source_fill_tagstruct(struct pa_tagstruct *t, struct pa_source *sour pa_tagstruct_putu32(t, source->owner ? source->owner->index : (uint32_t) -1); pa_tagstruct_putu32(t, source->monitor_of ? source->monitor_of->index : (uint32_t) -1); pa_tagstruct_puts(t, source->monitor_of ? source->monitor_of->name : ""); + pa_tagstruct_put_usec(t, pa_source_get_latency(source)); } static void client_fill_tagstruct(struct pa_tagstruct *t, struct pa_client *client) { @@ -1076,6 +1132,8 @@ static void source_output_fill_tagstruct(struct pa_tagstruct *t, struct pa_sourc pa_tagstruct_putu32(t, s->client ? s->client->index : (uint32_t) -1); pa_tagstruct_putu32(t, s->source->index); pa_tagstruct_put_sample_spec(t, &s->sample_spec); + pa_tagstruct_put_usec(t, pa_source_output_get_latency(s)); + pa_tagstruct_put_usec(t, pa_source_get_latency(s->source)); } static void scache_fill_tagstruct(struct pa_tagstruct *t, struct pa_scache_entry *e) { diff --git a/polyp/protocol-simple.c b/polyp/protocol-simple.c index 58156329..a7bd76fb 100644 --- a/polyp/protocol-simple.c +++ b/polyp/protocol-simple.c @@ -248,6 +248,12 @@ static void source_output_kill_cb(struct pa_source_output *o) { connection_free((struct connection *) o->userdata); } +static pa_usec_t source_output_get_latency_cb(struct pa_source_output *o) { + struct connection*c = o->userdata; + assert(o && c); + return pa_bytes_to_usec(pa_memblockq_get_length(c->output_memblockq), &c->source_output->sample_spec); +} + /*** client callbacks ***/ static void client_kill_cb(struct pa_client *c) { @@ -348,6 +354,7 @@ static void on_connection(struct pa_socket_server*s, struct pa_iochannel *io, vo c->source_output->push = source_output_push_cb; c->source_output->kill = source_output_kill_cb; + c->source_output->get_latency = source_output_get_latency_cb; c->source_output->userdata = c; l = (size_t) (pa_bytes_per_second(&p->sample_spec)*RECORD_BUFFER_SECONDS); diff --git a/polyp/sink.c b/polyp/sink.c index 6d3b59c7..8133d65a 100644 --- a/polyp/sink.c +++ b/polyp/sink.c @@ -197,7 +197,10 @@ int pa_sink_render(struct pa_sink*s, size_t length, struct pa_memchunk *result) unsigned n; size_t l; int r = -1; - assert(s && s->ref >= 1 && length && result); + assert(s); + assert(s->ref >= 1); + assert(length); + assert(result); pa_sink_ref(s); diff --git a/polyp/source-output.c b/polyp/source-output.c index 2566ec87..1db88d3c 100644 --- a/polyp/source-output.c +++ b/polyp/source-output.c @@ -60,6 +60,8 @@ struct pa_source_output* pa_source_output_new(struct pa_source *s, const char *n o->push = NULL; o->kill = NULL; o->userdata = NULL; + o->get_latency = NULL; + o->resampler = resampler; assert(s->core); @@ -149,3 +151,12 @@ void pa_source_output_set_name(struct pa_source_output *o, const char *name) { pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index); } + +pa_usec_t pa_source_output_get_latency(struct pa_source_output *o) { + assert(o && o->ref >= 1); + + if (o->get_latency) + return o->get_latency(o); + + return 0; +} diff --git a/polyp/source-output.h b/polyp/source-output.h index 709d65ad..ed09b537 100644 --- a/polyp/source-output.h +++ b/polyp/source-output.h @@ -50,6 +50,7 @@ struct pa_source_output { void (*push)(struct pa_source_output *o, const struct pa_memchunk *chunk); void (*kill)(struct pa_source_output* o); + pa_usec_t (*get_latency) (struct pa_source_output *i); struct pa_resampler* resampler; @@ -70,4 +71,7 @@ void pa_source_output_push(struct pa_source_output *o, const struct pa_memchunk void pa_source_output_set_name(struct pa_source_output *i, const char *name); +pa_usec_t pa_source_output_get_latency(struct pa_source_output *i); + + #endif diff --git a/polyp/source.c b/polyp/source.c index 2c0caca0..23b8bf8a 100644 --- a/polyp/source.c +++ b/polyp/source.c @@ -60,6 +60,7 @@ struct pa_source* pa_source_new(struct pa_core *core, const char *name, int fail s->outputs = pa_idxset_new(NULL, NULL); s->monitor_of = NULL; + s->get_latency = NULL; s->notify = NULL; s->userdata = NULL; @@ -150,3 +151,13 @@ void pa_source_set_owner(struct pa_source *s, struct pa_module *m) { assert(s); s->owner = m; } + +pa_usec_t pa_source_get_latency(struct pa_source *s) { + assert(s && s->ref >= 1); + + if (!s->get_latency) + return 0; + + return s->get_latency(s); +} + diff --git a/polyp/source.h b/polyp/source.h index 8b03c0d5..b6262835 100644 --- a/polyp/source.h +++ b/polyp/source.h @@ -53,6 +53,7 @@ struct pa_source { struct pa_sink *monitor_of; void (*notify)(struct pa_source*source); + pa_usec_t (*get_latency)(struct pa_source *s); void *userdata; }; @@ -68,4 +69,6 @@ void pa_source_notify(struct pa_source *s); void pa_source_set_owner(struct pa_source *s, struct pa_module *m); +pa_usec_t pa_source_get_latency(struct pa_source *s); + #endif -- cgit