]> code.delx.au - pulseaudio/blobdiff - src/pulsecore/sink-input.c
dbus: memory leak, actually free dbus wrapper
[pulseaudio] / src / pulsecore / sink-input.c
index 34217c86726f39db3479e1b9530686703094ef40..58559775c21c978accebd6ca37516ce91f18b2e4 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
 
   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
   or (at your option) any later version.
 
   PulseAudio is distributed in the hope that it will be useful, but
@@ -117,7 +117,8 @@ static void reset_callbacks(pa_sink_input *i) {
     i->attach = NULL;
     i->detach = NULL;
     i->suspend = NULL;
     i->attach = NULL;
     i->detach = NULL;
     i->suspend = NULL;
-    i->moved = NULL;
+    i->suspend_within_thread = NULL;
+    i->moving = NULL;
     i->kill = NULL;
     i->get_latency = NULL;
     i->state_change = NULL;
     i->kill = NULL;
     i->get_latency = NULL;
     i->state_change = NULL;
@@ -158,7 +159,6 @@ int pa_sink_input_new(
     pa_return_val_if_fail(data->sink, -PA_ERR_NOENTITY);
     pa_return_val_if_fail(PA_SINK_IS_LINKED(pa_sink_get_state(data->sink)), -PA_ERR_BADSTATE);
     pa_return_val_if_fail(!data->sync_base || (data->sync_base->sink == data->sink && pa_sink_input_get_state(data->sync_base) == PA_SINK_INPUT_CORKED), -PA_ERR_INVALID);
     pa_return_val_if_fail(data->sink, -PA_ERR_NOENTITY);
     pa_return_val_if_fail(PA_SINK_IS_LINKED(pa_sink_get_state(data->sink)), -PA_ERR_BADSTATE);
     pa_return_val_if_fail(!data->sync_base || (data->sync_base->sink == data->sink && pa_sink_input_get_state(data->sync_base) == PA_SINK_INPUT_CORKED), -PA_ERR_INVALID);
-    pa_return_val_if_fail(!(flags & PA_SINK_INPUT_FAIL_ON_SUSPEND) || pa_sink_get_state(data->sink) != PA_SINK_SUSPENDED, -PA_ERR_BADSTATE);
 
     if (!data->sample_spec_is_set)
         data->sample_spec = data->sink->sample_spec;
 
     if (!data->sample_spec_is_set)
         data->sample_spec = data->sink->sample_spec;
@@ -228,6 +228,12 @@ int pa_sink_input_new(
     if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], data)) < 0)
         return r;
 
     if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], data)) < 0)
         return r;
 
+    if ((flags & PA_SINK_INPUT_FAIL_ON_SUSPEND) &&
+        pa_sink_get_state(data->sink) == PA_SINK_SUSPENDED) {
+        pa_log_warn("Failed to create sink input: sink is suspended.");
+        return -PA_ERR_BADSTATE;
+    }
+
     if (pa_idxset_size(data->sink->inputs) >= PA_MAX_INPUTS_PER_SINK) {
         pa_log_warn("Failed to create sink input: too many inputs per sink.");
         return -PA_ERR_TOOLARGE;
     if (pa_idxset_size(data->sink->inputs) >= PA_MAX_INPUTS_PER_SINK) {
         pa_log_warn("Failed to create sink input: too many inputs per sink.");
         return -PA_ERR_TOOLARGE;
@@ -282,6 +288,7 @@ int pa_sink_input_new(
 
     i->volume_factor = data->volume_factor;
     pa_cvolume_init(&i->soft_volume);
 
     i->volume_factor = data->volume_factor;
     pa_cvolume_init(&i->soft_volume);
+    memset(i->relative_volume, 0, sizeof(i->relative_volume));
     i->save_volume = data->save_volume;
     i->save_sink = data->save_sink;
     i->save_muted = data->save_muted;
     i->save_volume = data->save_volume;
     i->save_sink = data->save_sink;
     i->save_muted = data->save_muted;
@@ -463,6 +470,8 @@ void pa_sink_input_unlink(pa_sink_input *i) {
         i->sink = NULL;
     }
 
         i->sink = NULL;
     }
 
+    pa_core_maybe_vacuum(i->core);
+
     pa_sink_input_unref(i);
 }
 
     pa_sink_input_unref(i);
 }
 
@@ -522,7 +531,7 @@ void pa_sink_input_put(pa_sink_input *i) {
         pa_sink_update_flat_volume(i->sink, &new_volume);
         pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE);
     } else
         pa_sink_update_flat_volume(i->sink, &new_volume);
         pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE);
     } else
-        pa_sw_cvolume_multiply(&i->soft_volume, &i->virtual_volume, &i->volume_factor);
+        pa_sink_input_set_relative_volume(i, &i->virtual_volume);
 
     i->thread_info.soft_volume = i->soft_volume;
     i->thread_info.muted = i->muted;
 
     i->thread_info.soft_volume = i->soft_volume;
     i->thread_info.muted = i->muted;
@@ -624,7 +633,7 @@ void pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, p
              * data, so let's just hand out silence */
             pa_atomic_store(&i->thread_info.drained, 1);
 
              * data, so let's just hand out silence */
             pa_atomic_store(&i->thread_info.drained, 1);
 
-            pa_memblockq_seek(i->thread_info.render_memblockq, (int64_t) slength, PA_SEEK_RELATIVE);
+            pa_memblockq_seek(i->thread_info.render_memblockq, (int64_t) slength, PA_SEEK_RELATIVE, TRUE);
             i->thread_info.playing_for = 0;
             if (i->thread_info.underrun_for != (uint64_t) -1)
                 i->thread_info.underrun_for += ilength;
             i->thread_info.playing_for = 0;
             if (i->thread_info.underrun_for != (uint64_t) -1)
                 i->thread_info.underrun_for += ilength;
@@ -769,7 +778,7 @@ void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sam
 
             if (amount > 0)
                 /* Ok, now update the write pointer */
 
             if (amount > 0)
                 /* Ok, now update the write pointer */
-                pa_memblockq_seek(i->thread_info.render_memblockq, - ((int64_t) amount), PA_SEEK_RELATIVE);
+                pa_memblockq_seek(i->thread_info.render_memblockq, - ((int64_t) amount), PA_SEEK_RELATIVE, TRUE);
 
             if (i->thread_info.rewrite_flush)
                 pa_memblockq_silence(i->thread_info.render_memblockq);
 
             if (i->thread_info.rewrite_flush)
                 pa_memblockq_silence(i->thread_info.render_memblockq);
@@ -811,27 +820,13 @@ void pa_sink_input_update_max_request(pa_sink_input *i, size_t nbytes  /* in the
         i->update_max_request(i, i->thread_info.resampler ? pa_resampler_request(i->thread_info.resampler, nbytes) : nbytes);
 }
 
         i->update_max_request(i, i->thread_info.resampler ? pa_resampler_request(i->thread_info.resampler, nbytes) : nbytes);
 }
 
-/* Called from thread context */
-static pa_usec_t fixup_latency(pa_sink *s, pa_usec_t usec) {
-    pa_sink_assert_ref(s);
-
-    if (usec == (pa_usec_t) -1)
-        return usec;
-
-    if (s->thread_info.max_latency > 0 && usec > s->thread_info.max_latency)
-        usec = s->thread_info.max_latency;
-
-    if (s->thread_info.min_latency > 0 && usec < s->thread_info.min_latency)
-        usec = s->thread_info.min_latency;
-
-    return usec;
-}
-
 /* Called from thread context */
 pa_usec_t pa_sink_input_set_requested_latency_within_thread(pa_sink_input *i, pa_usec_t usec) {
     pa_sink_input_assert_ref(i);
 
 /* Called from thread context */
 pa_usec_t pa_sink_input_set_requested_latency_within_thread(pa_sink_input *i, pa_usec_t usec) {
     pa_sink_input_assert_ref(i);
 
-    usec = fixup_latency(i->sink, usec);
+    if (usec != (pa_usec_t) -1)
+        usec =  PA_CLAMP(usec, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
+
     i->thread_info.requested_sink_latency = usec;
     pa_sink_invalidate_requested_latency(i->sink);
 
     i->thread_info.requested_sink_latency = usec;
     pa_sink_invalidate_requested_latency(i->sink);
 
@@ -840,33 +835,44 @@ pa_usec_t pa_sink_input_set_requested_latency_within_thread(pa_sink_input *i, pa
 
 /* Called from main context */
 pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec) {
 
 /* Called from main context */
 pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec) {
+    pa_usec_t min_latency, max_latency;
+
     pa_sink_input_assert_ref(i);
 
     pa_sink_input_assert_ref(i);
 
-    if (PA_SINK_INPUT_IS_LINKED(i->state))
+    if (PA_SINK_INPUT_IS_LINKED(i->state) && i->sink) {
         pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
         pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
-    else
-        /* If this sink input is not realized yet, we have to touch
-         * the thread info data directly */
+        return usec;
+    }
+
+    /* If this sink input is not realized yet or we are being moved,
+     * we have to touch the thread info data directly */
 
 
-        i->thread_info.requested_sink_latency = usec;
+    if (i->sink) {
+        pa_sink_get_latency_range(i->sink, &min_latency, &max_latency);
+
+        if (usec != (pa_usec_t) -1)
+            usec =  PA_CLAMP(usec, min_latency, max_latency);
+    }
+
+    i->thread_info.requested_sink_latency = usec;
 
     return usec;
 }
 
 /* Called from main context */
 pa_usec_t pa_sink_input_get_requested_latency(pa_sink_input *i) {
 
     return usec;
 }
 
 /* Called from main context */
 pa_usec_t pa_sink_input_get_requested_latency(pa_sink_input *i) {
-    pa_usec_t usec = 0;
-
     pa_sink_input_assert_ref(i);
 
     pa_sink_input_assert_ref(i);
 
-    if (PA_SINK_INPUT_IS_LINKED(i->state))
+    if (PA_SINK_INPUT_IS_LINKED(i->state) && i->sink) {
+        pa_usec_t usec = 0;
         pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
         pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
-    else
-        /* If this sink input is not realized yet, we have to touch
-         * the thread info data directly */
-        usec = i->thread_info.requested_sink_latency;
+        return usec;
+    }
 
 
-    return usec;
+    /* If this sink input is not realized yet or we are being moved,
+     * we have to touch the thread info data directly */
+
+    return i->thread_info.requested_sink_latency;
 }
 
 /* Called from main context */
 }
 
 /* Called from main context */
@@ -896,11 +902,12 @@ void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_boo
 
         /* OK, we are in normal volume mode. The volume only affects
          * ourselves */
 
         /* OK, we are in normal volume mode. The volume only affects
          * ourselves */
-        pa_sw_cvolume_multiply(&i->soft_volume, volume, &i->volume_factor);
+        pa_sink_input_set_relative_volume(i, volume);
 
         /* Hooks have the ability to play games with i->soft_volume */
         pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME], i);
 
 
         /* Hooks have the ability to play games with i->soft_volume */
         pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME], i);
 
+        /* Copy the new soft_volume to the thread_info struct */
         pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME, NULL, 0, NULL) == 0);
     }
 
         pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME, NULL, 0, NULL) == 0);
     }
 
@@ -916,6 +923,54 @@ const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i) {
     return &i->virtual_volume;
 }
 
     return &i->virtual_volume;
 }
 
+/* Called from main context */
+pa_cvolume *pa_sink_input_get_relative_volume(pa_sink_input *i, pa_cvolume *v) {
+    unsigned c;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert(v);
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+
+    /* This always returns a relative volume, even in flat volume mode */
+
+    v->channels = i->sample_spec.channels;
+
+    for (c = 0; c < v->channels; c++)
+        v->values[c] = pa_sw_volume_from_linear(i->relative_volume[c]);
+
+    return v;
+}
+
+/* Called from main context */
+void pa_sink_input_set_relative_volume(pa_sink_input *i, const pa_cvolume *v) {
+    unsigned c;
+    pa_cvolume _v;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+    pa_assert(!v || pa_cvolume_compatible(v, &i->sample_spec));
+
+    if (!v)
+        v = pa_cvolume_reset(&_v, i->sample_spec.channels);
+
+    /* This basically calculates:
+     *
+     * i->relative_volume := v
+     * i->soft_volume := i->relative_volume * i->volume_factor */
+
+    i->soft_volume.channels = i->sample_spec.channels;
+
+    for (c = 0; c < i->sample_spec.channels; c++) {
+        i->relative_volume[c] = pa_sw_volume_to_linear(v->values[c]);
+
+        i->soft_volume.values[c] = pa_sw_volume_from_linear(
+                i->relative_volume[c] *
+                pa_sw_volume_to_linear(i->volume_factor.values[c]));
+    }
+
+    /* We don't copy the data to the thread_info data. That's left for someone else to do */
+}
+
 /* Called from main context */
 void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute, pa_bool_t save) {
     pa_assert(i);
 /* Called from main context */
 void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute, pa_bool_t save) {
     pa_assert(i);
@@ -1083,9 +1138,11 @@ int pa_sink_input_start_move(pa_sink_input *i) {
     if (i->sink->flags & PA_SINK_FLAT_VOLUME) {
         pa_cvolume new_volume;
 
     if (i->sink->flags & PA_SINK_FLAT_VOLUME) {
         pa_cvolume new_volume;
 
-        /* Make the absolute volume relative */
-        i->virtual_volume = i->soft_volume;
-        i->soft_volume = i->volume_factor;
+        /* Make the virtual volume relative */
+        pa_sink_input_get_relative_volume(i, &i->virtual_volume);
+
+        /* And reset the the relative volume */
+        pa_sink_input_set_relative_volume(i, NULL);
 
         /* We might need to update the sink's volume if we are in flat
          * volume mode. */
 
         /* We might need to update the sink's volume if we are in flat
          * volume mode. */
@@ -1140,6 +1197,9 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
     } else
         new_resampler = NULL;
 
     } else
         new_resampler = NULL;
 
+    if (i->moving)
+        i->moving(i, dest);
+
     i->sink = dest;
     i->save_sink = save;
     pa_idxset_put(dest->inputs, i, NULL);
     i->sink = dest;
     i->save_sink = save;
     pa_idxset_put(dest->inputs, i, NULL);
@@ -1166,7 +1226,6 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
                 0,
                 &i->sink->silence);
     }
                 0,
                 &i->sink->silence);
     }
-
     pa_sink_update_status(dest);
 
     if (i->sink->flags & PA_SINK_FLAT_VOLUME) {
     pa_sink_update_status(dest);
 
     if (i->sink->flags & PA_SINK_FLAT_VOLUME) {
@@ -1187,9 +1246,6 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
     pa_log_debug("Successfully moved sink input %i to %s.", i->index, dest->name);
 
     /* Notify everyone */
     pa_log_debug("Successfully moved sink input %i to %s.", i->index, dest->name);
 
     /* Notify everyone */
-    if (i->moved)
-        i->moved(i);
-
     pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH], i);
     pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
 
     pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH], i);
     pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);