]> code.delx.au - pulseaudio/blobdiff - src/pulse/stream.c
protocol-native: Allow clients to know at what index underrun occurred
[pulseaudio] / src / pulse / stream.c
index cd5182ab4e5173bf87607229ce7040b9151d929a..416efb7f295634c8a98cc958cd3de2d011bd6e8b 100644 (file)
@@ -86,14 +86,16 @@ static pa_stream *pa_stream_new_with_proplist_internal(
         const pa_sample_spec *ss,
         const pa_channel_map *map,
         pa_format_info * const *formats,
+        unsigned int n_formats,
         pa_proplist *p) {
 
     pa_stream *s;
-    int i;
+    unsigned int i;
 
     pa_assert(c);
     pa_assert(PA_REFCNT_VALUE(c) >= 1);
-    pa_assert((ss == NULL && map == NULL) || formats == NULL);
+    pa_assert((ss == NULL && map == NULL) || (formats == NULL && n_formats == 0));
+    pa_assert(n_formats < PA_MAX_FORMATS);
 
     PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
     PA_CHECK_VALIDITY_RETURN_NULL(c, name || (p && pa_proplist_contains(p, PA_PROP_MEDIA_NAME)), PA_ERR_INVALID);
@@ -119,12 +121,9 @@ static pa_stream *pa_stream_new_with_proplist_internal(
 
     s->n_formats = 0;
     if (formats) {
-        for (i = 0; formats[i] && i < PA_MAX_FORMATS; i++) {
-            s->n_formats++;
+        s->n_formats = n_formats;
+        for (i = 0; i < n_formats; i++)
             s->req_formats[i] = pa_format_info_copy(formats[i]);
-        }
-        /* Make sure the input array was NULL-terminated */
-        pa_assert(formats[i] == NULL);
     }
 
     /* We'll get the final negotiated format after connecting */
@@ -181,6 +180,7 @@ static pa_stream *pa_stream_new_with_proplist_internal(
     s->timing_info_valid = FALSE;
 
     s->previous_time = 0;
+    s->latest_underrun_at_index = -1;
 
     s->read_index_not_before = 0;
     s->write_index_not_before = 0;
@@ -221,18 +221,19 @@ pa_stream *pa_stream_new_with_proplist(
     if (!map)
         PA_CHECK_VALIDITY_RETURN_NULL(c, map = pa_channel_map_init_auto(&tmap, ss->channels, PA_CHANNEL_MAP_DEFAULT), PA_ERR_INVALID);
 
-    return pa_stream_new_with_proplist_internal(c, name, ss, map, NULL, p);
+    return pa_stream_new_with_proplist_internal(c, name, ss, map, NULL, 0, p);
 }
 
 pa_stream *pa_stream_new_extended(
         pa_context *c,
         const char *name,
         pa_format_info * const *formats,
+        unsigned int n_formats,
         pa_proplist *p) {
 
     PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 21, PA_ERR_NOTSUPPORTED);
 
-    return pa_stream_new_with_proplist_internal(c, name, NULL, NULL, formats, p);
+    return pa_stream_new_with_proplist_internal(c, name, NULL, NULL, formats, n_formats, p);
 }
 
 static void stream_unlink(pa_stream *s) {
@@ -303,7 +304,10 @@ static void stream_free(pa_stream *s) {
         pa_smoother_free(s->smoother);
 
     for (i = 0; i < s->n_formats; i++)
-        pa_xfree(s->req_formats[i]);
+        pa_format_info_free(s->req_formats[i]);
+
+    if (s->format)
+        pa_format_info_free(s->format);
 
     pa_xfree(s->device_name);
     pa_xfree(s);
@@ -787,11 +791,10 @@ void pa_command_stream_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, p
         goto finish;
 
     if (pa_streq(event, PA_STREAM_EVENT_FORMAT_LOST)) {
-        /* Let client know what the running time was when the stream had to be
-         * killed  */
-        pa_usec_t time;
-        if (pa_stream_get_time(s, &time) == 0)
-            pa_proplist_setf(pl, "stream-time", "%llu", (unsigned long long) time);
+        /* Let client know what the running time was when the stream had to be killed  */
+        pa_usec_t stream_time;
+        if (pa_stream_get_time(s, &stream_time) == 0)
+            pa_proplist_setf(pl, "stream-time", "%llu", (unsigned long long) stream_time);
     }
 
     if (s->event_callback)
@@ -841,10 +844,17 @@ finish:
     pa_context_unref(c);
 }
 
+int64_t pa_stream_get_underflow_index(pa_stream *p)
+{
+    pa_assert(p);
+    return p->latest_underrun_at_index;
+}
+
 void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
     pa_stream *s;
     pa_context *c = userdata;
     uint32_t channel;
+    int64_t offset = -1;
 
     pa_assert(pd);
     pa_assert(command == PA_COMMAND_OVERFLOW || command == PA_COMMAND_UNDERFLOW);
@@ -854,8 +864,19 @@ void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, uint32
 
     pa_context_ref(c);
 
-    if (pa_tagstruct_getu32(t, &channel) < 0 ||
-        !pa_tagstruct_eof(t)) {
+    if (pa_tagstruct_getu32(t, &channel) < 0) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (c->version >= 23 && command == PA_COMMAND_UNDERFLOW) {
+        if (pa_tagstruct_gets64(t, &offset) < 0) {
+            pa_context_fail(c, PA_ERR_PROTOCOL);
+            goto finish;
+        }
+    }
+
+    if (!pa_tagstruct_eof(t)) {
         pa_context_fail(c, PA_ERR_PROTOCOL);
         goto finish;
     }
@@ -866,6 +887,9 @@ void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, uint32
     if (s->state != PA_STREAM_READY)
         goto finish;
 
+    if (offset != -1)
+        s->latest_underrun_at_index = offset;
+
     if (s->buffer_attr.prebuf > 0)
         check_smoother_status(s, TRUE, FALSE, TRUE);
 
@@ -1089,7 +1113,9 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag,
             s->timing_info.configured_sink_usec = usec;
     }
 
-    if (s->context->version >= 21 && s->direction == PA_STREAM_PLAYBACK) {
+    if ((s->context->version >= 21 && s->direction == PA_STREAM_PLAYBACK)
+        || s->context->version >= 22) {
+
         pa_format_info *f = pa_format_info_new();
         pa_tagstruct_get_format_info(t, f);
 
@@ -1144,7 +1170,8 @@ static int create_stream(
 
     pa_tagstruct *t;
     uint32_t tag;
-    pa_bool_t volume_set = FALSE;
+    pa_bool_t volume_set = !!volume;
+    pa_cvolume cv;
     uint32_t i;
 
     pa_assert(s);
@@ -1241,9 +1268,18 @@ static int create_stream(
             PA_TAG_BOOLEAN, s->corked,
             PA_TAG_INVALID);
 
-    if (s->direction == PA_STREAM_PLAYBACK) {
-        pa_cvolume cv;
+    if (!volume) {
+        if (pa_sample_spec_valid(&s->sample_spec))
+            volume = pa_cvolume_reset(&cv, s->sample_spec.channels);
+        else {
+            /* This is not really relevant, since no volume was set, and
+             * the real number of channels is embedded in the format_info
+             * structure */
+            volume = pa_cvolume_reset(&cv, PA_CHANNELS_MAX);
+        }
+    }
 
+    if (s->direction == PA_STREAM_PLAYBACK) {
         pa_tagstruct_put(
                 t,
                 PA_TAG_U32, s->buffer_attr.tlength,
@@ -1252,19 +1288,6 @@ static int create_stream(
                 PA_TAG_U32, s->syncid,
                 PA_TAG_INVALID);
 
-        volume_set = !!volume;
-
-        if (!volume) {
-            if (pa_sample_spec_valid(&s->sample_spec))
-                volume = pa_cvolume_reset(&cv, s->sample_spec.channels);
-            else {
-                /* This is not really relevant, since no volume was set, and
-                 * the real number of channels is embedded in the format_info
-                 * structure */
-                volume = pa_cvolume_reset(&cv, PA_CHANNELS_MAX);
-            }
-        }
-
         pa_tagstruct_put_cvolume(t, volume);
     } else
         pa_tagstruct_putu32(t, s->buffer_attr.fragsize);
@@ -1316,26 +1339,27 @@ static int create_stream(
         pa_tagstruct_put_boolean(t, flags & PA_STREAM_FAIL_ON_SUSPEND);
     }
 
-    if (s->context->version >= 17) {
+    if (s->context->version >= 17 && s->direction == PA_STREAM_PLAYBACK)
+        pa_tagstruct_put_boolean(t, flags & PA_STREAM_RELATIVE_VOLUME);
 
-        if (s->direction == PA_STREAM_PLAYBACK)
-            pa_tagstruct_put_boolean(t, flags & PA_STREAM_RELATIVE_VOLUME);
+    if (s->context->version >= 18 && s->direction == PA_STREAM_PLAYBACK)
+        pa_tagstruct_put_boolean(t, flags & (PA_STREAM_PASSTHROUGH));
 
-    }
+    if ((s->context->version >= 21 && s->direction == PA_STREAM_PLAYBACK)
+        || s->context->version >= 22) {
 
-    if (s->context->version >= 18) {
-
-        if (s->direction == PA_STREAM_PLAYBACK)
-            pa_tagstruct_put_boolean(t, flags & (PA_STREAM_PASSTHROUGH));
+        pa_tagstruct_putu8(t, s->n_formats);
+        for (i = 0; i < s->n_formats; i++)
+            pa_tagstruct_put_format_info(t, s->req_formats[i]);
     }
 
-    if (s->context->version >= 21) {
-
-        if (s->direction == PA_STREAM_PLAYBACK) {
-            pa_tagstruct_putu8(t, s->n_formats);
-            for (i = 0; i < s->n_formats; i++)
-                pa_tagstruct_put_format_info(t, s->req_formats[i]);
-        }
+    if (s->context->version >= 22 && s->direction == PA_STREAM_RECORD) {
+        pa_tagstruct_put_cvolume(t, volume);
+        pa_tagstruct_put_boolean(t, flags & PA_STREAM_START_MUTED);
+        pa_tagstruct_put_boolean(t, volume_set);
+        pa_tagstruct_put_boolean(t, flags & (PA_STREAM_START_MUTED|PA_STREAM_START_UNMUTED));
+        pa_tagstruct_put_boolean(t, flags & PA_STREAM_RELATIVE_VOLUME);
+        pa_tagstruct_put_boolean(t, flags & (PA_STREAM_PASSTHROUGH));
     }
 
     pa_pstream_send_tagstruct(s->context->pstream, t);
@@ -1651,7 +1675,7 @@ pa_operation * pa_stream_drain(pa_stream *s, pa_stream_success_cb_t cb, void *us
     pa_pstream_send_tagstruct(s->context->pstream, t);
     pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
 
-    /* This might cause the read index to conitnue again, hence
+    /* This might cause the read index to continue again, hence
      * let's request a timing update */
     request_auto_timing_update(s, TRUE);