add input latency measurement
authorLennart Poettering <lennart@poettering.net>
Thu, 16 Sep 2004 00:05:56 +0000 (00:05 +0000)
committerLennart Poettering <lennart@poettering.net>
Thu, 16 Sep 2004 00:05:56 +0000 (00:05 +0000)
add GETOSPACE support to module-oss

git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@205 fefdeb5f-60dc-0310-8127-8f9354f1896f

20 files changed:
doc/todo
polyp/cli-text.c
polyp/iochannel.c
polyp/iochannel.h
polyp/module-alsa-source.c
polyp/module-oss.c
polyp/native-common.h
polyp/pacat.c
polyp/polyplib-def.h
polyp/polyplib-introspect.c
polyp/polyplib-introspect.h
polyp/polyplib-stream.c
polyp/protocol-esound.c
polyp/protocol-native.c
polyp/protocol-simple.c
polyp/sink.c
polyp/source-output.c
polyp/source-output.h
polyp/source.c
polyp/source.h

index 22fa442137d488ef185a25fca95c34ccc0dcf0cb..675e8ca38779fa4369f5c389a3f1fb01eec6f5b6 100644 (file)
--- 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)
index 0915be8b0c4577b650d9f38466ce661a193679a5..9932e5689dcce522fbf33460e11d7d8553cffdb1 100644 (file)
@@ -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) 
index 1aa70b935d383421a7ce8c29343957dac17154fe..72bdac20e96820e956fb126c52c25209e1cc2189 100644 (file)
@@ -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);
+}
index 6f5f351c231269f0176389233706a3eaee8d9dca..a4edbfad5e4d6903c4a4acec20bdf1e957bf6821 100644 (file)
@@ -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);
index cf828eb0aeda85ce90921c418eee9470c3047e24..41a17691fc6591626ecde29c84669e4fc077f29e 100644 (file)
@@ -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);
 
index a45f72b8ca0211c77baf65c50e409996686460d0..68918604fc62682de7400e56373a163535c9fc70 100644 (file)
@@ -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
index 45e0b1d3508b320b4d69735b910e886396ad814a..a052fca2796d8bb87fe7854a12ea25d1e8148e72 100644 (file)
@@ -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
 };
 
index cc7d55f4064ca2573fb6da63c6b2f23b287b037c..933b0c3a7cb8e121b0d6bbf968cc3ce39714cfbe 100644 (file)
@@ -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));
 }
index 9bba3f32d6f73f692ce5c497d5b129b6b3c90543..29f5eb43a553c68fee33c870ccf656334d124ac2 100644 (file)
@@ -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
index 919adb9cc4ef63a721d712216c37c8b87d4c4622..1673be9bfc5f3bcdacebe0f911b87bfcafa2c907 100644 (file)
@@ -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;
             }
index 51210457d8e6f4dcebf27658d780531c502f2bc3..28c51feddbb01c2fc622fda565ccd49d632b3cd3 100644 (file)
@@ -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 */
index 89a8d33834fbaa8843f90db01105a0500edf991e..532d1700f2f9633c96f351a810a77abd3c4dbdbf 100644 (file)
@@ -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);
 
index 5abe474d6fde84995e4e5bc2c3d57fa496aae4e0..aff45099227e4f6bff2d3e17c72e5ef47359d2a8 100644 (file)
@@ -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) {
index be7b76f6fa04e2a41e0f1c5bf0df30596e5683b5..e197d1e2bdeff35f567b8df17d39264f5cdb9f47 100644 (file)
@@ -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) {
index 58156329c3bbd36c2e3f09d343c4461551078bb9..a7bd76fbd7b958c2f0d30f320eec7ed1c0dbe8c2 100644 (file)
@@ -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);
index 6d3b59c77b16d35ca4d034396431966d259119c7..8133d65a23e38610b9fbd10938f4d9809f990cc3 100644 (file)
@@ -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);
     
index 2566ec8756fdda4389f013461ae86e19fa2340c2..1db88d3cc04b41d2060f050fc59ef8dbbd941a91 100644 (file)
@@ -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;
+}
index 709d65ad970d48dfe23432535775c04d18ad348c..ed09b53706b1575be4e18370dbf8aa2fa832a870 100644 (file)
@@ -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
index 2c0caca077b884e8f39acf050e96225543cb01fc..23b8bf8ace67a620b732491102b09a1d7771e500 100644 (file)
@@ -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);
+}
+
index 8b03c0d569f5f2d9ec96904cd4a21e53f75f089e..b6262835f09f750f74922c1fb0faa1c7901d6cde 100644 (file)
@@ -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