]> code.delx.au - pulseaudio/blobdiff - src/pulsecore/sink.c
Merge commit 'origin/master-tx'
[pulseaudio] / src / pulsecore / sink.c
index 7441e9717c6608f22b9f589a0dfc016853f2c30d..a0f0ea7e6a82205bb4907b252700b16657f876e1 100644 (file)
@@ -6,7 +6,7 @@
 
   PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
+  by the Free Software Foundation; either version 2.1 of the License,
   or (at your option) any later version.
 
   PulseAudio is distributed in the hope that it will be useful, but
@@ -33,6 +33,7 @@
 #include <pulse/xmalloc.h>
 #include <pulse/timeval.h>
 #include <pulse/util.h>
+#include <pulse/i18n.h>
 
 #include <pulsecore/sink-input.h>
 #include <pulsecore/namereg.h>
@@ -47,7 +48,8 @@
 
 #define MAX_MIX_CHANNELS 32
 #define MIX_BUFFER_LENGTH (PA_PAGE_SIZE)
-#define DEFAULT_MIN_LATENCY (4*PA_USEC_PER_MSEC)
+#define ABSOLUTE_MIN_LATENCY (500)
+#define ABSOLUTE_MAX_LATENCY (10*PA_USEC_PER_SEC)
 
 static PA_DEFINE_CHECK_TYPE(pa_sink, pa_msgobject);
 
@@ -172,6 +174,9 @@ pa_sink* pa_sink_new(
     if (data->card)
         pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->card->proplist);
 
+    pa_device_init_description(data->proplist);
+    pa_device_init_icon(data->proplist, TRUE);
+
     if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_FIXATE], data) < 0) {
         pa_xfree(s);
         pa_namereg_unregister(core, name);
@@ -226,8 +231,8 @@ pa_sink* pa_sink_new(
     s->thread_info.max_request = 0;
     s->thread_info.requested_latency_valid = FALSE;
     s->thread_info.requested_latency = 0;
-    s->thread_info.min_latency = DEFAULT_MIN_LATENCY;
-    s->thread_info.max_latency = 0;
+    s->thread_info.min_latency = ABSOLUTE_MIN_LATENCY;
+    s->thread_info.max_latency = ABSOLUTE_MAX_LATENCY;
 
     pa_assert_se(pa_idxset_put(core->sinks, s, &s->index) >= 0);
 
@@ -255,7 +260,7 @@ pa_sink* pa_sink_new(
     pa_proplist_setf(source_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Monitor of %s", dn ? dn : s->name);
     pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_CLASS, "monitor");
 
-    s->monitor_source = pa_source_new(core, &source_data, PA_SOURCE_LATENCY);
+    s->monitor_source = pa_source_new(core, &source_data, 0);
 
     pa_source_new_data_done(&source_data);
 
@@ -322,6 +327,9 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
                 pa_sink_input_kill(i);
             else if (i->suspend)
                 i->suspend(i, state == PA_SINK_SUSPENDED);
+
+        if (s->monitor_source)
+            pa_source_sync_suspend(s->monitor_source);
     }
 
     return 0;
@@ -336,8 +344,7 @@ void pa_sink_put(pa_sink* s) {
     /* The following fields must be initialized properly when calling _put() */
     pa_assert(s->asyncmsgq);
     pa_assert(s->rtpoll);
-    pa_assert(!s->thread_info.min_latency || !s->thread_info.max_latency ||
-              s->thread_info.min_latency <= s->thread_info.max_latency);
+    pa_assert(s->thread_info.min_latency <= s->thread_info.max_latency);
 
     if (!(s->flags & PA_SINK_HW_VOLUME_CTRL)) {
         s->flags |= PA_SINK_DECIBEL_VOLUME;
@@ -353,6 +360,12 @@ void pa_sink_put(pa_sink* s) {
         if (s->flags & PA_SINK_DECIBEL_VOLUME)
             s->flags |= PA_SINK_FLAT_VOLUME;
 
+    if (s->flags & PA_SINK_LATENCY)
+        s->monitor_source->flags |= PA_SOURCE_LATENCY;
+
+    if (s->flags & PA_SINK_DYNAMIC_LATENCY)
+        s->monitor_source->flags |= PA_SOURCE_DYNAMIC_LATENCY;
+
     pa_assert_se(sink_set_state(s, PA_SINK_IDLE) == 0);
 
     pa_source_put(s->monitor_source);
@@ -945,6 +958,32 @@ pa_usec_t pa_sink_get_latency(pa_sink *s) {
     return usec;
 }
 
+/* Called from IO thread */
+pa_usec_t pa_sink_get_latency_within_thread(pa_sink *s) {
+    pa_usec_t usec = 0;
+    pa_msgobject *o;
+
+    pa_sink_assert_ref(s);
+    pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
+
+    /* The returned value is supposed to be in the time domain of the sound card! */
+
+    if (s->thread_info.state == PA_SINK_SUSPENDED)
+        return 0;
+
+    if (!(s->flags & PA_SINK_LATENCY))
+        return 0;
+
+    o = PA_MSGOBJECT(s);
+
+    /* We probably should make this a proper vtable callback instead of going through process_msg() */
+
+    if (o->process_msg(o, PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
+        return -1;
+
+    return usec;
+}
+
 /* Called from main thread */
 void pa_sink_update_flat_volume(pa_sink *s, pa_cvolume *new_volume) {
     pa_sink_input *i;
@@ -1127,6 +1166,19 @@ const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_bool_t force_refresh) {
     return &s->virtual_volume;
 }
 
+/* Called from main thread */
+void pa_sink_volume_changed(pa_sink *s, const pa_cvolume *new_volume) {
+    pa_sink_assert_ref(s);
+
+    /* The sink implementor may call this if the volume changed to make sure everyone is notified */
+
+    if (pa_cvolume_equal(&s->virtual_volume, new_volume))
+        return;
+
+    s->virtual_volume = *new_volume;
+    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+}
+
 /* Called from main thread */
 void pa_sink_set_mute(pa_sink *s, pa_bool_t mute) {
     pa_bool_t old_muted;
@@ -1167,12 +1219,24 @@ pa_bool_t pa_sink_get_mute(pa_sink *s, pa_bool_t force_refresh) {
 }
 
 /* Called from main thread */
-pa_bool_t pa_sink_update_proplist(pa_sink *s, pa_update_mode_t mode, pa_proplist *p) {
+void pa_sink_mute_changed(pa_sink *s, pa_bool_t new_muted) {
+    pa_sink_assert_ref(s);
 
+    /* The sink implementor may call this if the volume changed to make sure everyone is notified */
+
+    if (s->muted == new_muted)
+        return;
+
+    s->muted = new_muted;
+    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+}
+
+/* Called from main thread */
+pa_bool_t pa_sink_update_proplist(pa_sink *s, pa_update_mode_t mode, pa_proplist *p) {
     pa_sink_assert_ref(s);
-    pa_assert(p);
 
-    pa_proplist_update(s->proplist, mode, p);
+    if (p)
+        pa_proplist_update(s->proplist, mode, p);
 
     if (PA_SINK_IS_LINKED(s->state)) {
         pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], s);
@@ -1525,8 +1589,10 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
 
             s->thread_info.state = PA_PTR_TO_UINT(userdata);
 
-            if (s->thread_info.state == PA_SINK_SUSPENDED)
+            if (s->thread_info.state == PA_SINK_SUSPENDED) {
+                s->thread_info.rewind_nbytes = 0;
                 s->thread_info.rewind_requested = FALSE;
+            }
 
             return 0;
 
@@ -1556,7 +1622,7 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
         case PA_SINK_MESSAGE_SET_LATENCY_RANGE: {
             pa_usec_t *r = userdata;
 
-            pa_sink_update_latency_range(s, r[0], r[1]);
+            pa_sink_set_latency_range_within_thread(s, r[0], r[1]);
 
             return 0;
         }
@@ -1580,6 +1646,16 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
             *((size_t*) userdata) = s->thread_info.max_request;
             return 0;
 
+        case PA_SINK_MESSAGE_SET_MAX_REWIND:
+
+            pa_sink_set_max_rewind_within_thread(s, (size_t) offset);
+            return 0;
+
+        case PA_SINK_MESSAGE_SET_MAX_REQUEST:
+
+            pa_sink_set_max_request_within_thread(s, (size_t) offset);
+            return 0;
+
         case PA_SINK_MESSAGE_GET_LATENCY:
         case PA_SINK_MESSAGE_MAX:
             ;
@@ -1596,8 +1672,12 @@ int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend) {
 
     pa_core_assert_ref(c);
 
-    for (sink = PA_SINK(pa_idxset_first(c->sinks, &idx)); sink; sink = PA_SINK(pa_idxset_next(c->sinks, &idx)))
-        ret -= pa_sink_suspend(sink, suspend) < 0;
+    for (sink = PA_SINK(pa_idxset_first(c->sinks, &idx)); sink; sink = PA_SINK(pa_idxset_next(c->sinks, &idx))) {
+        int r;
+
+        if ((r = pa_sink_suspend(sink, suspend)) < 0)
+            ret = r;
+    }
 
     return ret;
 }
@@ -1699,10 +1779,10 @@ pa_usec_t pa_sink_get_requested_latency_within_thread(pa_sink *s) {
         result = monitor_latency;
 
     if (result != (pa_usec_t) -1) {
-        if (s->thread_info.max_latency > 0 && result > s->thread_info.max_latency)
+        if (result > s->thread_info.max_latency)
             result = s->thread_info.max_latency;
 
-        if (s->thread_info.min_latency > 0 && result < s->thread_info.min_latency)
+        if (result < s->thread_info.min_latency)
             result = s->thread_info.min_latency;
     }
 
@@ -1726,8 +1806,8 @@ pa_usec_t pa_sink_get_requested_latency(pa_sink *s) {
     return usec;
 }
 
-/* Called from IO thread */
-void pa_sink_set_max_rewind(pa_sink *s, size_t max_rewind) {
+/* Called from IO as well as the main thread -- the latter only before the IO thread started up */
+void pa_sink_set_max_rewind_within_thread(pa_sink *s, size_t max_rewind) {
     pa_sink_input *i;
     void *state = NULL;
 
@@ -1744,11 +1824,21 @@ void pa_sink_set_max_rewind(pa_sink *s, size_t max_rewind) {
     }
 
     if (s->monitor_source)
-        pa_source_set_max_rewind(s->monitor_source, s->thread_info.max_rewind);
+        pa_source_set_max_rewind_within_thread(s->monitor_source, s->thread_info.max_rewind);
 }
 
-/* Called from IO thread */
-void pa_sink_set_max_request(pa_sink *s, size_t max_request) {
+/* Called from main thread */
+void pa_sink_set_max_rewind(pa_sink *s, size_t max_rewind) {
+    pa_sink_assert_ref(s);
+
+    if (PA_SINK_IS_LINKED(s->state))
+        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MAX_REWIND, NULL, max_rewind, NULL) == 0);
+    else
+        pa_sink_set_max_rewind_within_thread(s, max_rewind);
+}
+
+/* Called from IO as well as the main thread -- the latter only before the IO thread started up */
+void pa_sink_set_max_request_within_thread(pa_sink *s, size_t max_request) {
     void *state = NULL;
 
     pa_sink_assert_ref(s);
@@ -1766,6 +1856,16 @@ void pa_sink_set_max_request(pa_sink *s, size_t max_request) {
     }
 }
 
+/* Called from main thread */
+void pa_sink_set_max_request(pa_sink *s, size_t max_request) {
+    pa_sink_assert_ref(s);
+
+    if (PA_SINK_IS_LINKED(s->state))
+        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MAX_REQUEST, NULL, max_request, NULL) == 0);
+    else
+        pa_sink_set_max_request_within_thread(s, max_request);
+}
+
 /* Called from IO thread */
 void pa_sink_invalidate_requested_latency(pa_sink *s) {
     pa_sink_input *i;
@@ -1775,12 +1875,15 @@ void pa_sink_invalidate_requested_latency(pa_sink *s) {
 
     s->thread_info.requested_latency_valid = FALSE;
 
-    if (s->update_requested_latency)
-        s->update_requested_latency(s);
+    if (PA_SINK_IS_LINKED(s->thread_info.state)) {
 
-    while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
-        if (i->update_sink_requested_latency)
-            i->update_sink_requested_latency(i);
+        if (s->update_requested_latency)
+            s->update_requested_latency(s);
+
+        while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+            if (i->update_sink_requested_latency)
+                i->update_sink_requested_latency(i);
+    }
 }
 
 /* Called from main thread */
@@ -1788,19 +1891,23 @@ void pa_sink_set_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_
     pa_sink_assert_ref(s);
 
     /* min_latency == 0:           no limit
-     * min_latency == (size_t) -1: default limit
      * min_latency anything else:  specified limit
      *
      * Similar for max_latency */
 
-    if (min_latency == (pa_usec_t) -1)
-        min_latency = DEFAULT_MIN_LATENCY;
+    if (min_latency < ABSOLUTE_MIN_LATENCY)
+        min_latency = ABSOLUTE_MIN_LATENCY;
 
-    if (max_latency == (pa_usec_t) -1)
-        max_latency = min_latency;
+    if (max_latency <= 0 ||
+        max_latency > ABSOLUTE_MAX_LATENCY)
+        max_latency = ABSOLUTE_MAX_LATENCY;
 
-    pa_assert(!min_latency || !max_latency ||
-              min_latency <= max_latency);
+    pa_assert(min_latency <= max_latency);
+
+    /* Hmm, let's see if someone forgot to set PA_SINK_DYNAMIC_LATENCY here... */
+    pa_assert((min_latency == ABSOLUTE_MIN_LATENCY &&
+               max_latency == ABSOLUTE_MAX_LATENCY) ||
+              (s->flags & PA_SINK_DYNAMIC_LATENCY));
 
     if (PA_SINK_IS_LINKED(s->state)) {
         pa_usec_t r[2];
@@ -1809,15 +1916,8 @@ void pa_sink_set_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_
         r[1] = max_latency;
 
         pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_LATENCY_RANGE, r, 0, NULL) == 0);
-    } else {
-        s->thread_info.min_latency = min_latency;
-        s->thread_info.max_latency = max_latency;
-
-        s->monitor_source->thread_info.min_latency = min_latency;
-        s->monitor_source->thread_info.max_latency = max_latency;
-
-        s->thread_info.requested_latency_valid = s->monitor_source->thread_info.requested_latency_valid = FALSE;
-    }
+    } else
+        pa_sink_set_latency_range_within_thread(s, min_latency, max_latency);
 }
 
 /* Called from main thread */
@@ -1840,25 +1940,34 @@ void pa_sink_get_latency_range(pa_sink *s, pa_usec_t *min_latency, pa_usec_t *ma
 }
 
 /* Called from IO thread */
-void pa_sink_update_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency) {
-    pa_sink_input *i;
+void pa_sink_set_latency_range_within_thread(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency) {
     void *state = NULL;
 
     pa_sink_assert_ref(s);
 
-    pa_assert(!min_latency || !max_latency ||
-              min_latency <= max_latency);
+    pa_assert(min_latency >= ABSOLUTE_MIN_LATENCY);
+    pa_assert(max_latency <= ABSOLUTE_MAX_LATENCY);
+    pa_assert(min_latency <= max_latency);
+
+    /* Hmm, let's see if someone forgot to set PA_SINK_DYNAMIC_LATENCY here... */
+    pa_assert((min_latency == ABSOLUTE_MIN_LATENCY &&
+               max_latency == ABSOLUTE_MAX_LATENCY) ||
+              (s->flags & PA_SINK_DYNAMIC_LATENCY));
 
     s->thread_info.min_latency = min_latency;
     s->thread_info.max_latency = max_latency;
 
-    while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
-        if (i->update_sink_latency_range)
-            i->update_sink_latency_range(i);
+    if (PA_SINK_IS_LINKED(s->thread_info.state)) {
+        pa_sink_input *i;
+
+        while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+            if (i->update_sink_latency_range)
+                i->update_sink_latency_range(i);
+    }
 
     pa_sink_invalidate_requested_latency(s);
 
-    pa_source_update_latency_range(s->monitor_source, min_latency, max_latency);
+    pa_source_set_latency_range_within_thread(s->monitor_source, min_latency, max_latency);
 }
 
 /* Called from main context */
@@ -1886,3 +1995,83 @@ size_t pa_sink_get_max_request(pa_sink *s) {
 
     return r;
 }
+
+/* Called from main context */
+pa_bool_t pa_device_init_icon(pa_proplist *p, pa_bool_t is_sink) {
+    const char *ff, *c, *t = NULL, *s = "", *profile, *bus;
+
+    pa_assert(p);
+
+    if (pa_proplist_contains(p, PA_PROP_DEVICE_ICON_NAME))
+        return TRUE;
+
+    if ((ff = pa_proplist_gets(p, PA_PROP_DEVICE_FORM_FACTOR))) {
+
+        if (pa_streq(ff, "microphone"))
+            t = "audio-input-microphone";
+        else if (pa_streq(ff, "webcam"))
+            t = "camera-web";
+        else if (pa_streq(ff, "computer"))
+            t = "computer";
+        else if (pa_streq(ff, "handset"))
+            t = "phone";
+        else if (pa_streq(ff, "portable"))
+            t = "multimedia-player";
+        else if (pa_streq(ff, "tv"))
+            t = "video-display";
+    }
+
+    if (!t)
+        if ((c = pa_proplist_gets(p, PA_PROP_DEVICE_CLASS)))
+            if (pa_streq(c, "modem"))
+                t = "modem";
+
+    if (!t) {
+        if (is_sink)
+            t = "audio-card";
+        else
+            t = "audio-input-microphone";
+    }
+
+    if ((profile = pa_proplist_gets(p, PA_PROP_DEVICE_PROFILE_NAME))) {
+        if (strstr(profile, "analog"))
+            s = "-analog";
+        else if (strstr(profile, "iec958"))
+            s = "-iec958";
+        else if (strstr(profile, "hdmi"))
+            s = "-hdmi";
+    }
+
+    bus = pa_proplist_gets(p, PA_PROP_DEVICE_BUS);
+
+    pa_proplist_setf(p, PA_PROP_DEVICE_ICON_NAME, "%s%s%s%s", t, pa_strempty(s), bus ? "-" : "", pa_strempty(bus));
+
+    return TRUE;
+}
+
+pa_bool_t pa_device_init_description(pa_proplist *p) {
+    const char *s;
+    pa_assert(p);
+
+    if (pa_proplist_contains(p, PA_PROP_DEVICE_DESCRIPTION))
+        return TRUE;
+
+    if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_FORM_FACTOR)))
+        if (pa_streq(s, "internal")) {
+            pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, _("Internal Audio"));
+            return TRUE;
+        }
+
+    if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_CLASS)))
+        if (pa_streq(s, "modem")) {
+            pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, _("Modem"));
+            return TRUE;
+        }
+
+    if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_PRODUCT_NAME))) {
+        pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, s);
+        return TRUE;
+    }
+
+    return FALSE;
+}