Merge commit 'coling/master'
[platform/upstream/pulseaudio.git] / src / pulsecore / sink.c
1 /***
2   This file is part of PulseAudio.
3
4   Copyright 2004-2006 Lennart Poettering
5   Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
6
7   PulseAudio is free software; you can redistribute it and/or modify
8   it under the terms of the GNU Lesser General Public License as published
9   by the Free Software Foundation; either version 2 of the License,
10   or (at your option) any later version.
11
12   PulseAudio is distributed in the hope that it will be useful, but
13   WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   General Public License for more details.
16
17   You should have received a copy of the GNU Lesser General Public License
18   along with PulseAudio; if not, write to the Free Software
19   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20   USA.
21 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <stdlib.h>
28 #include <string.h>
29 #include <stdio.h>
30
31 #include <pulse/introspect.h>
32 #include <pulse/utf8.h>
33 #include <pulse/xmalloc.h>
34 #include <pulse/timeval.h>
35 #include <pulse/util.h>
36 #include <pulse/i18n.h>
37
38 #include <pulsecore/sink-input.h>
39 #include <pulsecore/namereg.h>
40 #include <pulsecore/core-util.h>
41 #include <pulsecore/sample-util.h>
42 #include <pulsecore/core-subscribe.h>
43 #include <pulsecore/log.h>
44 #include <pulsecore/macro.h>
45 #include <pulsecore/play-memblockq.h>
46
47 #include "sink.h"
48
49 #define MAX_MIX_CHANNELS 32
50 #define MIX_BUFFER_LENGTH (PA_PAGE_SIZE)
51 #define DEFAULT_MIN_LATENCY (4*PA_USEC_PER_MSEC)
52
53 static PA_DEFINE_CHECK_TYPE(pa_sink, pa_msgobject);
54
55 static void sink_free(pa_object *s);
56
57 pa_sink_new_data* pa_sink_new_data_init(pa_sink_new_data *data) {
58     pa_assert(data);
59
60     memset(data, 0, sizeof(*data));
61     data->proplist = pa_proplist_new();
62
63     return data;
64 }
65
66 void pa_sink_new_data_set_name(pa_sink_new_data *data, const char *name) {
67     pa_assert(data);
68
69     pa_xfree(data->name);
70     data->name = pa_xstrdup(name);
71 }
72
73 void pa_sink_new_data_set_sample_spec(pa_sink_new_data *data, const pa_sample_spec *spec) {
74     pa_assert(data);
75
76     if ((data->sample_spec_is_set = !!spec))
77         data->sample_spec = *spec;
78 }
79
80 void pa_sink_new_data_set_channel_map(pa_sink_new_data *data, const pa_channel_map *map) {
81     pa_assert(data);
82
83     if ((data->channel_map_is_set = !!map))
84         data->channel_map = *map;
85 }
86
87 void pa_sink_new_data_set_volume(pa_sink_new_data *data, const pa_cvolume *volume) {
88     pa_assert(data);
89
90     if ((data->volume_is_set = !!volume))
91         data->volume = *volume;
92 }
93
94 void pa_sink_new_data_set_muted(pa_sink_new_data *data, pa_bool_t mute) {
95     pa_assert(data);
96
97     data->muted_is_set = TRUE;
98     data->muted = !!mute;
99 }
100
101 void pa_sink_new_data_done(pa_sink_new_data *data) {
102     pa_assert(data);
103
104     pa_xfree(data->name);
105     pa_proplist_free(data->proplist);
106 }
107
108 /* Called from main context */
109 static void reset_callbacks(pa_sink *s) {
110     pa_assert(s);
111
112     s->set_state = NULL;
113     s->get_volume = NULL;
114     s->set_volume = NULL;
115     s->get_mute = NULL;
116     s->set_mute = NULL;
117     s->request_rewind = NULL;
118     s->update_requested_latency = NULL;
119 }
120
121 /* Called from main context */
122 pa_sink* pa_sink_new(
123         pa_core *core,
124         pa_sink_new_data *data,
125         pa_sink_flags_t flags) {
126
127     pa_sink *s;
128     const char *name;
129     char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
130     pa_source_new_data source_data;
131     const char *dn;
132     char *pt;
133
134     pa_assert(core);
135     pa_assert(data);
136     pa_assert(data->name);
137
138     s = pa_msgobject_new(pa_sink);
139
140     if (!(name = pa_namereg_register(core, data->name, PA_NAMEREG_SINK, s, data->namereg_fail))) {
141         pa_xfree(s);
142         return NULL;
143     }
144
145     pa_sink_new_data_set_name(data, name);
146
147     if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_NEW], data) < 0) {
148         pa_xfree(s);
149         pa_namereg_unregister(core, name);
150         return NULL;
151     }
152
153     pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver));
154     pa_return_null_if_fail(data->name && pa_utf8_valid(data->name) && data->name[0]);
155
156     pa_return_null_if_fail(data->sample_spec_is_set && pa_sample_spec_valid(&data->sample_spec));
157
158     if (!data->channel_map_is_set)
159         pa_return_null_if_fail(pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT));
160
161     pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map));
162     pa_return_null_if_fail(data->channel_map.channels == data->sample_spec.channels);
163
164     if (!data->volume_is_set)
165         pa_cvolume_reset(&data->volume, data->sample_spec.channels);
166
167     pa_return_null_if_fail(pa_cvolume_valid(&data->volume));
168     pa_return_null_if_fail(data->volume.channels == data->sample_spec.channels);
169
170     if (!data->muted_is_set)
171         data->muted = FALSE;
172
173     if (data->card)
174         pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->card->proplist);
175
176     pa_device_init_description(data->proplist);
177     pa_device_init_icon(data->proplist, TRUE);
178
179     if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_FIXATE], data) < 0) {
180         pa_xfree(s);
181         pa_namereg_unregister(core, name);
182         return NULL;
183     }
184
185     s->parent.parent.free = sink_free;
186     s->parent.process_msg = pa_sink_process_msg;
187
188     s->core = core;
189     s->state = PA_SINK_INIT;
190     s->flags = flags;
191     s->name = pa_xstrdup(name);
192     s->proplist = pa_proplist_copy(data->proplist);
193     s->driver = pa_xstrdup(pa_path_get_filename(data->driver));
194     s->module = data->module;
195     s->card = data->card;
196
197     s->sample_spec = data->sample_spec;
198     s->channel_map = data->channel_map;
199
200     s->inputs = pa_idxset_new(NULL, NULL);
201     s->n_corked = 0;
202
203     s->virtual_volume = data->volume;
204     pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels);
205     s->base_volume = PA_VOLUME_NORM;
206     s->n_volume_steps = PA_VOLUME_NORM+1;
207     s->muted = data->muted;
208     s->refresh_volume = s->refresh_muted = FALSE;
209
210     reset_callbacks(s);
211     s->userdata = NULL;
212
213     s->asyncmsgq = NULL;
214     s->rtpoll = NULL;
215
216     pa_silence_memchunk_get(
217             &core->silence_cache,
218             core->mempool,
219             &s->silence,
220             &s->sample_spec,
221             0);
222
223     s->thread_info.inputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
224     s->thread_info.soft_volume =  s->soft_volume;
225     s->thread_info.soft_muted = s->muted;
226     s->thread_info.state = s->state;
227     s->thread_info.rewind_nbytes = 0;
228     s->thread_info.rewind_requested = FALSE;
229     s->thread_info.max_rewind = 0;
230     s->thread_info.max_request = 0;
231     s->thread_info.requested_latency_valid = FALSE;
232     s->thread_info.requested_latency = 0;
233     s->thread_info.min_latency = DEFAULT_MIN_LATENCY;
234     s->thread_info.max_latency = 0;
235
236     pa_assert_se(pa_idxset_put(core->sinks, s, &s->index) >= 0);
237
238     if (s->card)
239         pa_assert_se(pa_idxset_put(s->card->sinks, s, NULL) >= 0);
240
241     pt = pa_proplist_to_string_sep(s->proplist, "\n    ");
242     pa_log_info("Created sink %u \"%s\" with sample spec %s and channel map %s\n    %s",
243                 s->index,
244                 s->name,
245                 pa_sample_spec_snprint(st, sizeof(st), &s->sample_spec),
246                 pa_channel_map_snprint(cm, sizeof(cm), &s->channel_map),
247                 pt);
248     pa_xfree(pt);
249
250     pa_source_new_data_init(&source_data);
251     pa_source_new_data_set_sample_spec(&source_data, &s->sample_spec);
252     pa_source_new_data_set_channel_map(&source_data, &s->channel_map);
253     source_data.name = pa_sprintf_malloc("%s.monitor", name);
254     source_data.driver = data->driver;
255     source_data.module = data->module;
256     source_data.card = data->card;
257
258     dn = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
259     pa_proplist_setf(source_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Monitor of %s", dn ? dn : s->name);
260     pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_CLASS, "monitor");
261
262     s->monitor_source = pa_source_new(core, &source_data, PA_SOURCE_LATENCY);
263
264     pa_source_new_data_done(&source_data);
265
266     if (!s->monitor_source) {
267         pa_sink_unlink(s);
268         pa_sink_unref(s);
269         return NULL;
270     }
271
272     s->monitor_source->monitor_of = s;
273
274     pa_source_set_latency_range(s->monitor_source, s->thread_info.min_latency, s->thread_info.max_latency);
275     pa_source_set_max_rewind(s->monitor_source, s->thread_info.max_rewind);
276
277     return s;
278 }
279
280 /* Called from main context */
281 static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
282     int ret;
283     pa_bool_t suspend_change;
284     pa_sink_state_t original_state;
285
286     pa_assert(s);
287
288     if (s->state == state)
289         return 0;
290
291     original_state = s->state;
292
293     suspend_change =
294         (original_state == PA_SINK_SUSPENDED && PA_SINK_IS_OPENED(state)) ||
295         (PA_SINK_IS_OPENED(original_state) && state == PA_SINK_SUSPENDED);
296
297     if (s->set_state)
298         if ((ret = s->set_state(s, state)) < 0)
299             return ret;
300
301     if (s->asyncmsgq)
302         if ((ret = pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL)) < 0) {
303
304             if (s->set_state)
305                 s->set_state(s, original_state);
306
307             return ret;
308         }
309
310     s->state = state;
311
312     if (state != PA_SINK_UNLINKED) { /* if we enter UNLINKED state pa_sink_unlink() will fire the apropriate events */
313         pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], s);
314         pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
315     }
316
317     if (suspend_change) {
318         pa_sink_input *i;
319         uint32_t idx;
320
321         /* We're suspending or resuming, tell everyone about it */
322
323         for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx)))
324             if (s->state == PA_SINK_SUSPENDED &&
325                 (i->flags & PA_SINK_INPUT_FAIL_ON_SUSPEND))
326                 pa_sink_input_kill(i);
327             else if (i->suspend)
328                 i->suspend(i, state == PA_SINK_SUSPENDED);
329     }
330
331     return 0;
332 }
333
334 /* Called from main context */
335 void pa_sink_put(pa_sink* s) {
336     pa_sink_assert_ref(s);
337
338     pa_assert(s->state == PA_SINK_INIT);
339
340     /* The following fields must be initialized properly when calling _put() */
341     pa_assert(s->asyncmsgq);
342     pa_assert(s->rtpoll);
343     pa_assert(!s->thread_info.min_latency || !s->thread_info.max_latency ||
344               s->thread_info.min_latency <= s->thread_info.max_latency);
345
346     if (!(s->flags & PA_SINK_HW_VOLUME_CTRL)) {
347         s->flags |= PA_SINK_DECIBEL_VOLUME;
348
349         s->thread_info.soft_volume = s->soft_volume;
350         s->thread_info.soft_muted = s->muted;
351     }
352
353     if (s->flags & PA_SINK_DECIBEL_VOLUME)
354         s->n_volume_steps = PA_VOLUME_NORM+1;
355
356     if (s->core->flat_volumes)
357         if (s->flags & PA_SINK_DECIBEL_VOLUME)
358             s->flags |= PA_SINK_FLAT_VOLUME;
359
360     pa_assert_se(sink_set_state(s, PA_SINK_IDLE) == 0);
361
362     pa_source_put(s->monitor_source);
363
364     pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_NEW, s->index);
365     pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PUT], s);
366 }
367
368 /* Called from main context */
369 void pa_sink_unlink(pa_sink* s) {
370     pa_bool_t linked;
371     pa_sink_input *i, *j = NULL;
372
373     pa_assert(s);
374
375     /* Please note that pa_sink_unlink() does more than simply
376      * reversing pa_sink_put(). It also undoes the registrations
377      * already done in pa_sink_new()! */
378
379     /* All operations here shall be idempotent, i.e. pa_sink_unlink()
380      * may be called multiple times on the same sink without bad
381      * effects. */
382
383     linked = PA_SINK_IS_LINKED(s->state);
384
385     if (linked)
386         pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_UNLINK], s);
387
388     if (s->state != PA_SINK_UNLINKED)
389         pa_namereg_unregister(s->core, s->name);
390     pa_idxset_remove_by_data(s->core->sinks, s, NULL);
391
392     if (s->card)
393         pa_idxset_remove_by_data(s->card->sinks, s, NULL);
394
395     while ((i = pa_idxset_first(s->inputs, NULL))) {
396         pa_assert(i != j);
397         pa_sink_input_kill(i);
398         j = i;
399     }
400
401     if (linked)
402         sink_set_state(s, PA_SINK_UNLINKED);
403     else
404         s->state = PA_SINK_UNLINKED;
405
406     reset_callbacks(s);
407
408     if (s->monitor_source)
409         pa_source_unlink(s->monitor_source);
410
411     if (linked) {
412         pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_REMOVE, s->index);
413         pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_UNLINK_POST], s);
414     }
415 }
416
417 /* Called from main context */
418 static void sink_free(pa_object *o) {
419     pa_sink *s = PA_SINK(o);
420     pa_sink_input *i;
421
422     pa_assert(s);
423     pa_assert(pa_sink_refcnt(s) == 0);
424
425     if (PA_SINK_IS_LINKED(s->state))
426         pa_sink_unlink(s);
427
428     pa_log_info("Freeing sink %u \"%s\"", s->index, s->name);
429
430     if (s->monitor_source) {
431         pa_source_unref(s->monitor_source);
432         s->monitor_source = NULL;
433     }
434
435     pa_idxset_free(s->inputs, NULL, NULL);
436
437     while ((i = pa_hashmap_steal_first(s->thread_info.inputs)))
438         pa_sink_input_unref(i);
439
440     pa_hashmap_free(s->thread_info.inputs, NULL, NULL);
441
442     if (s->silence.memblock)
443         pa_memblock_unref(s->silence.memblock);
444
445     pa_xfree(s->name);
446     pa_xfree(s->driver);
447
448     if (s->proplist)
449         pa_proplist_free(s->proplist);
450
451     pa_xfree(s);
452 }
453
454 /* Called from main context */
455 void pa_sink_set_asyncmsgq(pa_sink *s, pa_asyncmsgq *q) {
456     pa_sink_assert_ref(s);
457
458     s->asyncmsgq = q;
459
460     if (s->monitor_source)
461         pa_source_set_asyncmsgq(s->monitor_source, q);
462 }
463
464 /* Called from main context */
465 void pa_sink_set_rtpoll(pa_sink *s, pa_rtpoll *p) {
466     pa_sink_assert_ref(s);
467
468     s->rtpoll = p;
469     if (s->monitor_source)
470         pa_source_set_rtpoll(s->monitor_source, p);
471 }
472
473 /* Called from main context */
474 int pa_sink_update_status(pa_sink*s) {
475     pa_sink_assert_ref(s);
476     pa_assert(PA_SINK_IS_LINKED(s->state));
477
478     if (s->state == PA_SINK_SUSPENDED)
479         return 0;
480
481     return sink_set_state(s, pa_sink_used_by(s) ? PA_SINK_RUNNING : PA_SINK_IDLE);
482 }
483
484 /* Called from main context */
485 int pa_sink_suspend(pa_sink *s, pa_bool_t suspend) {
486     pa_sink_assert_ref(s);
487     pa_assert(PA_SINK_IS_LINKED(s->state));
488
489     if (suspend)
490         return sink_set_state(s, PA_SINK_SUSPENDED);
491     else
492         return sink_set_state(s, pa_sink_used_by(s) ? PA_SINK_RUNNING : PA_SINK_IDLE);
493 }
494
495 /* Called from main context */
496 pa_queue *pa_sink_move_all_start(pa_sink *s) {
497     pa_queue *q;
498     pa_sink_input *i, *n;
499     uint32_t idx;
500
501     pa_sink_assert_ref(s);
502     pa_assert(PA_SINK_IS_LINKED(s->state));
503
504     q = pa_queue_new();
505
506     for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = n) {
507         n = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx));
508
509         if (pa_sink_input_start_move(i) >= 0)
510             pa_queue_push(q, pa_sink_input_ref(i));
511     }
512
513     return q;
514 }
515
516 /* Called from main context */
517 void pa_sink_move_all_finish(pa_sink *s, pa_queue *q, pa_bool_t save) {
518     pa_sink_input *i;
519
520     pa_sink_assert_ref(s);
521     pa_assert(PA_SINK_IS_LINKED(s->state));
522     pa_assert(q);
523
524     while ((i = PA_SINK_INPUT(pa_queue_pop(q)))) {
525         if (pa_sink_input_finish_move(i, s, save) < 0)
526             pa_sink_input_kill(i);
527
528         pa_sink_input_unref(i);
529     }
530
531     pa_queue_free(q, NULL, NULL);
532 }
533
534 /* Called from main context */
535 void pa_sink_move_all_fail(pa_queue *q) {
536     pa_sink_input *i;
537     pa_assert(q);
538
539     while ((i = PA_SINK_INPUT(pa_queue_pop(q)))) {
540         if (pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FAIL], i) == PA_HOOK_OK) {
541             pa_sink_input_kill(i);
542             pa_sink_input_unref(i);
543         }
544     }
545
546     pa_queue_free(q, NULL, NULL);
547 }
548
549 /* Called from IO thread context */
550 void pa_sink_process_rewind(pa_sink *s, size_t nbytes) {
551     pa_sink_input *i;
552     void *state = NULL;
553     pa_sink_assert_ref(s);
554     pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
555
556     /* If nobody requested this and this is actually no real rewind
557      * then we can short cut this */
558     if (!s->thread_info.rewind_requested && nbytes <= 0)
559         return;
560
561     s->thread_info.rewind_nbytes = 0;
562     s->thread_info.rewind_requested = FALSE;
563
564     if (s->thread_info.state == PA_SINK_SUSPENDED)
565         return;
566
567     if (nbytes > 0)
568         pa_log_debug("Processing rewind...");
569
570     while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) {
571         pa_sink_input_assert_ref(i);
572         pa_sink_input_process_rewind(i, nbytes);
573     }
574
575     if (nbytes > 0)
576         if (s->monitor_source && PA_SOURCE_IS_LINKED(s->monitor_source->thread_info.state))
577             pa_source_process_rewind(s->monitor_source, nbytes);
578 }
579
580 /* Called from IO thread context */
581 static unsigned fill_mix_info(pa_sink *s, size_t *length, pa_mix_info *info, unsigned maxinfo) {
582     pa_sink_input *i;
583     unsigned n = 0;
584     void *state = NULL;
585     size_t mixlength = *length;
586
587     pa_sink_assert_ref(s);
588     pa_assert(info);
589
590     while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)) && maxinfo > 0) {
591         pa_sink_input_assert_ref(i);
592
593         pa_sink_input_peek(i, *length, &info->chunk, &info->volume);
594
595         if (mixlength == 0 || info->chunk.length < mixlength)
596             mixlength = info->chunk.length;
597
598         if (pa_memblock_is_silence(info->chunk.memblock)) {
599             pa_memblock_unref(info->chunk.memblock);
600             continue;
601         }
602
603         info->userdata = pa_sink_input_ref(i);
604
605         pa_assert(info->chunk.memblock);
606         pa_assert(info->chunk.length > 0);
607
608         info++;
609         n++;
610         maxinfo--;
611     }
612
613     if (mixlength > 0)
614         *length = mixlength;
615
616     return n;
617 }
618
619 /* Called from IO thread context */
620 static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned n, pa_memchunk *result) {
621     pa_sink_input *i;
622     void *state = NULL;
623     unsigned p = 0;
624     unsigned n_unreffed = 0;
625
626     pa_sink_assert_ref(s);
627     pa_assert(result);
628     pa_assert(result->memblock);
629     pa_assert(result->length > 0);
630
631     /* We optimize for the case where the order of the inputs has not changed */
632
633     while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) {
634         unsigned j;
635         pa_mix_info* m = NULL;
636
637         pa_sink_input_assert_ref(i);
638
639         /* Let's try to find the matching entry info the pa_mix_info array */
640         for (j = 0; j < n; j ++) {
641
642             if (info[p].userdata == i) {
643                 m = info + p;
644                 break;
645             }
646
647             p++;
648             if (p >= n)
649                 p = 0;
650         }
651
652         /* Drop read data */
653         pa_sink_input_drop(i, result->length);
654
655         if (s->monitor_source && PA_SOURCE_IS_LINKED(s->monitor_source->thread_info.state)) {
656
657             if (pa_hashmap_size(i->thread_info.direct_outputs) > 0) {
658                 void *ostate = NULL;
659                 pa_source_output *o;
660                 pa_memchunk c;
661
662                 if (m && m->chunk.memblock) {
663                     c = m->chunk;
664                     pa_memblock_ref(c.memblock);
665                     pa_assert(result->length <= c.length);
666                     c.length = result->length;
667
668                     pa_memchunk_make_writable(&c, 0);
669                     pa_volume_memchunk(&c, &s->sample_spec, &m->volume);
670                 } else {
671                     c = s->silence;
672                     pa_memblock_ref(c.memblock);
673                     pa_assert(result->length <= c.length);
674                     c.length = result->length;
675                 }
676
677                 while ((o = pa_hashmap_iterate(i->thread_info.direct_outputs, &ostate, NULL))) {
678                     pa_source_output_assert_ref(o);
679                     pa_assert(o->direct_on_input == i);
680                     pa_source_post_direct(s->monitor_source, o, &c);
681                 }
682
683                 pa_memblock_unref(c.memblock);
684             }
685         }
686
687         if (m) {
688             if (m->chunk.memblock)
689                 pa_memblock_unref(m->chunk.memblock);
690                 pa_memchunk_reset(&m->chunk);
691
692             pa_sink_input_unref(m->userdata);
693             m->userdata = NULL;
694
695             n_unreffed += 1;
696         }
697     }
698
699     /* Now drop references to entries that are included in the
700      * pa_mix_info array but don't exist anymore */
701
702     if (n_unreffed < n) {
703         for (; n > 0; info++, n--) {
704             if (info->userdata)
705                 pa_sink_input_unref(info->userdata);
706             if (info->chunk.memblock)
707                 pa_memblock_unref(info->chunk.memblock);
708         }
709     }
710
711     if (s->monitor_source && PA_SOURCE_IS_LINKED(s->monitor_source->thread_info.state))
712         pa_source_post(s->monitor_source, result);
713 }
714
715 /* Called from IO thread context */
716 void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
717     pa_mix_info info[MAX_MIX_CHANNELS];
718     unsigned n;
719     size_t block_size_max;
720
721     pa_sink_assert_ref(s);
722     pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
723     pa_assert(pa_frame_aligned(length, &s->sample_spec));
724     pa_assert(result);
725
726     pa_sink_ref(s);
727
728     pa_assert(!s->thread_info.rewind_requested);
729     pa_assert(s->thread_info.rewind_nbytes == 0);
730
731     if (s->thread_info.state == PA_SINK_SUSPENDED) {
732         result->memblock = pa_memblock_ref(s->silence.memblock);
733         result->index = s->silence.index;
734         result->length = PA_MIN(s->silence.length, length);
735         return;
736     }
737
738     if (length <= 0)
739         length = pa_frame_align(MIX_BUFFER_LENGTH, &s->sample_spec);
740
741     block_size_max = pa_mempool_block_size_max(s->core->mempool);
742     if (length > block_size_max)
743         length = pa_frame_align(block_size_max, &s->sample_spec);
744
745     pa_assert(length > 0);
746
747     n = fill_mix_info(s, &length, info, MAX_MIX_CHANNELS);
748
749     if (n == 0) {
750
751         *result = s->silence;
752         pa_memblock_ref(result->memblock);
753
754         if (result->length > length)
755             result->length = length;
756
757     } else if (n == 1) {
758         pa_cvolume volume;
759
760         *result = info[0].chunk;
761         pa_memblock_ref(result->memblock);
762
763         if (result->length > length)
764             result->length = length;
765
766         pa_sw_cvolume_multiply(&volume, &s->thread_info.soft_volume, &info[0].volume);
767
768         if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&volume)) {
769             pa_memchunk_make_writable(result, 0);
770             if (s->thread_info.soft_muted || pa_cvolume_is_muted(&volume))
771                 pa_silence_memchunk(result, &s->sample_spec);
772             else
773                 pa_volume_memchunk(result, &s->sample_spec, &volume);
774         }
775     } else {
776         void *ptr;
777         result->memblock = pa_memblock_new(s->core->mempool, length);
778
779         ptr = pa_memblock_acquire(result->memblock);
780         result->length = pa_mix(info, n,
781                                 ptr, length,
782                                 &s->sample_spec,
783                                 &s->thread_info.soft_volume,
784                                 s->thread_info.soft_muted);
785         pa_memblock_release(result->memblock);
786
787         result->index = 0;
788     }
789
790     inputs_drop(s, info, n, result);
791
792     pa_sink_unref(s);
793 }
794
795 /* Called from IO thread context */
796 void pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
797     pa_mix_info info[MAX_MIX_CHANNELS];
798     unsigned n;
799     size_t length, block_size_max;
800
801     pa_sink_assert_ref(s);
802     pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
803     pa_assert(target);
804     pa_assert(target->memblock);
805     pa_assert(target->length > 0);
806     pa_assert(pa_frame_aligned(target->length, &s->sample_spec));
807
808     pa_sink_ref(s);
809
810     pa_assert(!s->thread_info.rewind_requested);
811     pa_assert(s->thread_info.rewind_nbytes == 0);
812
813     if (s->thread_info.state == PA_SINK_SUSPENDED) {
814         pa_silence_memchunk(target, &s->sample_spec);
815         return;
816     }
817
818     length = target->length;
819     block_size_max = pa_mempool_block_size_max(s->core->mempool);
820     if (length > block_size_max)
821         length = pa_frame_align(block_size_max, &s->sample_spec);
822
823     pa_assert(length > 0);
824
825     n = fill_mix_info(s, &length, info, MAX_MIX_CHANNELS);
826
827     if (n == 0) {
828         if (target->length > length)
829             target->length = length;
830
831         pa_silence_memchunk(target, &s->sample_spec);
832     } else if (n == 1) {
833         pa_cvolume volume;
834
835         if (target->length > length)
836             target->length = length;
837
838         pa_sw_cvolume_multiply(&volume, &s->thread_info.soft_volume, &info[0].volume);
839
840         if (s->thread_info.soft_muted || pa_cvolume_is_muted(&volume))
841             pa_silence_memchunk(target, &s->sample_spec);
842         else {
843             pa_memchunk vchunk;
844
845             vchunk = info[0].chunk;
846             pa_memblock_ref(vchunk.memblock);
847
848             if (vchunk.length > length)
849                 vchunk.length = length;
850
851             if (!pa_cvolume_is_norm(&volume)) {
852                 pa_memchunk_make_writable(&vchunk, 0);
853                 pa_volume_memchunk(&vchunk, &s->sample_spec, &volume);
854             }
855
856             pa_memchunk_memcpy(target, &vchunk);
857             pa_memblock_unref(vchunk.memblock);
858         }
859
860     } else {
861         void *ptr;
862
863         ptr = pa_memblock_acquire(target->memblock);
864
865         target->length = pa_mix(info, n,
866                                 (uint8_t*) ptr + target->index, length,
867                                 &s->sample_spec,
868                                 &s->thread_info.soft_volume,
869                                 s->thread_info.soft_muted);
870
871         pa_memblock_release(target->memblock);
872     }
873
874     inputs_drop(s, info, n, target);
875
876     pa_sink_unref(s);
877 }
878
879 /* Called from IO thread context */
880 void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) {
881     pa_memchunk chunk;
882     size_t l, d;
883
884     pa_sink_assert_ref(s);
885     pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
886     pa_assert(target);
887     pa_assert(target->memblock);
888     pa_assert(target->length > 0);
889     pa_assert(pa_frame_aligned(target->length, &s->sample_spec));
890
891     pa_sink_ref(s);
892
893     pa_assert(!s->thread_info.rewind_requested);
894     pa_assert(s->thread_info.rewind_nbytes == 0);
895
896     l = target->length;
897     d = 0;
898     while (l > 0) {
899         chunk = *target;
900         chunk.index += d;
901         chunk.length -= d;
902
903         pa_sink_render_into(s, &chunk);
904
905         d += chunk.length;
906         l -= chunk.length;
907     }
908
909     pa_sink_unref(s);
910 }
911
912 /* Called from IO thread context */
913 void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) {
914     pa_sink_assert_ref(s);
915     pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
916     pa_assert(length > 0);
917     pa_assert(pa_frame_aligned(length, &s->sample_spec));
918     pa_assert(result);
919
920     pa_assert(!s->thread_info.rewind_requested);
921     pa_assert(s->thread_info.rewind_nbytes == 0);
922
923     /*** This needs optimization ***/
924
925     result->index = 0;
926     result->length = length;
927     result->memblock = pa_memblock_new(s->core->mempool, length);
928
929     pa_sink_render_into_full(s, result);
930 }
931
932 /* Called from main thread */
933 pa_usec_t pa_sink_get_latency(pa_sink *s) {
934     pa_usec_t usec = 0;
935
936     pa_sink_assert_ref(s);
937     pa_assert(PA_SINK_IS_LINKED(s->state));
938
939     /* The returned value is supposed to be in the time domain of the sound card! */
940
941     if (s->state == PA_SINK_SUSPENDED)
942         return 0;
943
944     if (!(s->flags & PA_SINK_LATENCY))
945         return 0;
946
947     pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) == 0);
948
949     return usec;
950 }
951
952 /* Called from main thread */
953 void pa_sink_update_flat_volume(pa_sink *s, pa_cvolume *new_volume) {
954     pa_sink_input *i;
955     uint32_t idx;
956
957     pa_sink_assert_ref(s);
958     pa_assert(new_volume);
959     pa_assert(PA_SINK_IS_LINKED(s->state));
960     pa_assert(s->flags & PA_SINK_FLAT_VOLUME);
961
962     /* This is called whenever a sink input volume changes and we
963      * might need to fix up the sink volume accordingly. Please note
964      * that we don't actually update the sinks volume here, we only
965      * return how it needs to be updated. The caller should then call
966      * pa_sink_set_flat_volume().*/
967
968     if (pa_idxset_isempty(s->inputs)) {
969         /* In the special case that we have no sink input we leave the
970          * volume unmodified. */
971         *new_volume = s->virtual_volume;
972         return;
973     }
974
975     pa_cvolume_mute(new_volume, s->channel_map.channels);
976
977     /* First let's determine the new maximum volume of all inputs
978      * connected to this sink */
979     for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) {
980         unsigned c;
981         pa_cvolume remapped_volume;
982
983         remapped_volume = i->virtual_volume;
984         pa_cvolume_remap(&remapped_volume, &i->channel_map, &s->channel_map);
985
986         for (c = 0; c < new_volume->channels; c++)
987             if (remapped_volume.values[c] > new_volume->values[c])
988                 new_volume->values[c] = remapped_volume.values[c];
989     }
990
991     /* Then, let's update the soft volumes of all inputs connected
992      * to this sink */
993     for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) {
994         pa_cvolume remapped_new_volume;
995
996         remapped_new_volume = *new_volume;
997         pa_cvolume_remap(&remapped_new_volume, &s->channel_map, &i->channel_map);
998         pa_sw_cvolume_divide(&i->soft_volume, &i->virtual_volume, &remapped_new_volume);
999         pa_sw_cvolume_multiply(&i->soft_volume, &i->soft_volume, &i->volume_factor);
1000
1001         /* Hooks have the ability to play games with i->soft_volume */
1002         pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME], i);
1003
1004         /* We don't issue PA_SINK_INPUT_MESSAGE_SET_VOLUME because
1005          * we want the update to have atomically with the sink
1006          * volume update, hence we do it within the
1007          * pa_sink_set_flat_volume() call below*/
1008     }
1009 }
1010
1011 /* Called from main thread */
1012 void pa_sink_propagate_flat_volume(pa_sink *s, const pa_cvolume *old_volume) {
1013     pa_sink_input *i;
1014     uint32_t idx;
1015
1016     pa_sink_assert_ref(s);
1017     pa_assert(old_volume);
1018     pa_assert(PA_SINK_IS_LINKED(s->state));
1019     pa_assert(s->flags & PA_SINK_FLAT_VOLUME);
1020
1021     /* This is called whenever the sink volume changes that is not
1022      * caused by a sink input volume change. We need to fix up the
1023      * sink input volumes accordingly */
1024
1025     for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) {
1026         pa_cvolume remapped_old_volume, remapped_new_volume, fixed_volume;
1027         unsigned c;
1028
1029         remapped_new_volume = s->virtual_volume;
1030         pa_cvolume_remap(&remapped_new_volume, &s->channel_map, &i->channel_map);
1031
1032         remapped_old_volume = *old_volume;
1033         pa_cvolume_remap(&remapped_old_volume, &s->channel_map, &i->channel_map);
1034
1035         for (c = 0; c < i->sample_spec.channels; c++)
1036
1037             if (remapped_old_volume.values[c] == PA_VOLUME_MUTED)
1038                 fixed_volume.values[c] = PA_VOLUME_MUTED;
1039             else
1040                 fixed_volume.values[c] = (pa_volume_t)
1041                     ((uint64_t) i->virtual_volume.values[c] *
1042                      (uint64_t) remapped_new_volume.values[c] /
1043                      (uint64_t) remapped_old_volume.values[c]);
1044
1045         fixed_volume.channels = i->virtual_volume.channels;
1046
1047         if (!pa_cvolume_equal(&fixed_volume, &i->virtual_volume)) {
1048             i->virtual_volume = fixed_volume;
1049
1050             /* The virtual volume changed, let's tell people so */
1051             pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
1052         }
1053     }
1054 }
1055
1056 /* Called from main thread */
1057 void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume, pa_bool_t propagate, pa_bool_t sendmsg) {
1058     pa_cvolume old_virtual_volume;
1059     pa_bool_t virtual_volume_changed;
1060
1061     pa_sink_assert_ref(s);
1062     pa_assert(PA_SINK_IS_LINKED(s->state));
1063     pa_assert(volume);
1064     pa_assert(pa_cvolume_valid(volume));
1065     pa_assert(pa_cvolume_compatible(volume, &s->sample_spec));
1066
1067     old_virtual_volume = s->virtual_volume;
1068     s->virtual_volume = *volume;
1069     virtual_volume_changed = !pa_cvolume_equal(&old_virtual_volume, &s->virtual_volume);
1070
1071     /* Propagate this volume change back to the inputs */
1072     if (virtual_volume_changed)
1073         if (propagate && (s->flags & PA_SINK_FLAT_VOLUME))
1074             pa_sink_propagate_flat_volume(s, &old_virtual_volume);
1075
1076     if (s->set_volume) {
1077         /* If we have a function set_volume(), then we do not apply a
1078          * soft volume by default. However, set_volume() is apply one
1079          * to s->soft_volume */
1080
1081         pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels);
1082         s->set_volume(s);
1083
1084     } else
1085         /* If we have no function set_volume(), then the soft volume
1086          * becomes the virtual volume */
1087         s->soft_volume = s->virtual_volume;
1088
1089     /* This tells the sink that soft and/or virtual volume changed */
1090     if (sendmsg)
1091         pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL) == 0);
1092
1093     if (virtual_volume_changed)
1094         pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
1095 }
1096
1097 /* Called from main thread. Only to be called by sink implementor */
1098 void pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume) {
1099     pa_sink_assert_ref(s);
1100     pa_assert(volume);
1101
1102     s->soft_volume = *volume;
1103
1104     if (PA_SINK_IS_LINKED(s->state))
1105         pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL) == 0);
1106     else
1107         s->thread_info.soft_volume = *volume;
1108 }
1109
1110 /* Called from main thread */
1111 const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_bool_t force_refresh) {
1112     pa_sink_assert_ref(s);
1113
1114     if (s->refresh_volume || force_refresh) {
1115         struct pa_cvolume old_virtual_volume = s->virtual_volume;
1116
1117         if (s->get_volume)
1118             s->get_volume(s);
1119
1120         pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_VOLUME, NULL, 0, NULL) == 0);
1121
1122         if (!pa_cvolume_equal(&old_virtual_volume, &s->virtual_volume)) {
1123
1124             if (s->flags & PA_SINK_FLAT_VOLUME)
1125                 pa_sink_propagate_flat_volume(s, &old_virtual_volume);
1126
1127             pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
1128         }
1129     }
1130
1131     return &s->virtual_volume;
1132 }
1133
1134 /* Called from main thread */
1135 void pa_sink_set_mute(pa_sink *s, pa_bool_t mute) {
1136     pa_bool_t old_muted;
1137
1138     pa_sink_assert_ref(s);
1139     pa_assert(PA_SINK_IS_LINKED(s->state));
1140
1141     old_muted = s->muted;
1142     s->muted = mute;
1143
1144     if (s->set_mute)
1145         s->set_mute(s);
1146
1147     pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0);
1148
1149     if (old_muted != s->muted)
1150         pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
1151 }
1152
1153 /* Called from main thread */
1154 pa_bool_t pa_sink_get_mute(pa_sink *s, pa_bool_t force_refresh) {
1155
1156     pa_sink_assert_ref(s);
1157
1158     if (s->refresh_muted || force_refresh) {
1159         pa_bool_t old_muted = s->muted;
1160
1161         if (s->get_mute)
1162             s->get_mute(s);
1163
1164         pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MUTE, NULL, 0, NULL) == 0);
1165
1166         if (old_muted != s->muted)
1167             pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
1168     }
1169
1170     return s->muted;
1171 }
1172
1173 /* Called from main thread */
1174 pa_bool_t pa_sink_update_proplist(pa_sink *s, pa_update_mode_t mode, pa_proplist *p) {
1175
1176     pa_sink_assert_ref(s);
1177     pa_assert(p);
1178
1179     pa_proplist_update(s->proplist, mode, p);
1180
1181     if (PA_SINK_IS_LINKED(s->state)) {
1182         pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], s);
1183         pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
1184     }
1185
1186     return TRUE;
1187 }
1188
1189 /* Called from main thread */
1190 void pa_sink_set_description(pa_sink *s, const char *description) {
1191     const char *old;
1192     pa_sink_assert_ref(s);
1193
1194     if (!description && !pa_proplist_contains(s->proplist, PA_PROP_DEVICE_DESCRIPTION))
1195         return;
1196
1197     old = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
1198
1199     if (old && description && !strcmp(old, description))
1200         return;
1201
1202     if (description)
1203         pa_proplist_sets(s->proplist, PA_PROP_DEVICE_DESCRIPTION, description);
1204     else
1205         pa_proplist_unset(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
1206
1207     if (s->monitor_source) {
1208         char *n;
1209
1210         n = pa_sprintf_malloc("Monitor Source of %s", description ? description : s->name);
1211         pa_source_set_description(s->monitor_source, n);
1212         pa_xfree(n);
1213     }
1214
1215     if (PA_SINK_IS_LINKED(s->state)) {
1216         pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
1217         pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], s);
1218     }
1219 }
1220
1221 /* Called from main thread */
1222 unsigned pa_sink_linked_by(pa_sink *s) {
1223     unsigned ret;
1224
1225     pa_sink_assert_ref(s);
1226     pa_assert(PA_SINK_IS_LINKED(s->state));
1227
1228     ret = pa_idxset_size(s->inputs);
1229
1230     /* We add in the number of streams connected to us here. Please
1231      * note the asymmmetry to pa_sink_used_by()! */
1232
1233     if (s->monitor_source)
1234         ret += pa_source_linked_by(s->monitor_source);
1235
1236     return ret;
1237 }
1238
1239 /* Called from main thread */
1240 unsigned pa_sink_used_by(pa_sink *s) {
1241     unsigned ret;
1242
1243     pa_sink_assert_ref(s);
1244     pa_assert(PA_SINK_IS_LINKED(s->state));
1245
1246     ret = pa_idxset_size(s->inputs);
1247     pa_assert(ret >= s->n_corked);
1248
1249     /* Streams connected to our monitor source do not matter for
1250      * pa_sink_used_by()!.*/
1251
1252     return ret - s->n_corked;
1253 }
1254
1255 /* Called from main thread */
1256 unsigned pa_sink_check_suspend(pa_sink *s) {
1257     unsigned ret;
1258     pa_sink_input *i;
1259     uint32_t idx;
1260
1261     pa_sink_assert_ref(s);
1262
1263     if (!PA_SINK_IS_LINKED(s->state))
1264         return 0;
1265
1266     ret = 0;
1267
1268     for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) {
1269         pa_sink_input_state_t st;
1270
1271         st = pa_sink_input_get_state(i);
1272         pa_assert(PA_SINK_INPUT_IS_LINKED(st));
1273
1274         if (st == PA_SINK_INPUT_CORKED)
1275             continue;
1276
1277         if (i->flags & PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND)
1278             continue;
1279
1280         ret ++;
1281     }
1282
1283     if (s->monitor_source)
1284         ret += pa_source_check_suspend(s->monitor_source);
1285
1286     return ret;
1287 }
1288
1289 /* Called from the IO thread */
1290 static void sync_input_volumes_within_thread(pa_sink *s) {
1291     pa_sink_input *i;
1292     void *state = NULL;
1293
1294     pa_sink_assert_ref(s);
1295
1296     while ((i = PA_SINK_INPUT(pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))) {
1297         if (pa_cvolume_equal(&i->thread_info.soft_volume, &i->soft_volume))
1298             continue;
1299
1300         i->thread_info.soft_volume = i->soft_volume;
1301         pa_sink_input_request_rewind(i, 0, TRUE, FALSE, FALSE);
1302     }
1303 }
1304
1305 /* Called from IO thread, except when it is not */
1306 int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
1307     pa_sink *s = PA_SINK(o);
1308     pa_sink_assert_ref(s);
1309
1310     switch ((pa_sink_message_t) code) {
1311
1312         case PA_SINK_MESSAGE_ADD_INPUT: {
1313             pa_sink_input *i = PA_SINK_INPUT(userdata);
1314
1315             /* If you change anything here, make sure to change the
1316              * sink input handling a few lines down at
1317              * PA_SINK_MESSAGE_FINISH_MOVE, too. */
1318
1319             pa_hashmap_put(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index), pa_sink_input_ref(i));
1320
1321             /* Since the caller sleeps in pa_sink_input_put(), we can
1322              * safely access data outside of thread_info even though
1323              * it is mutable */
1324
1325             if ((i->thread_info.sync_prev = i->sync_prev)) {
1326                 pa_assert(i->sink == i->thread_info.sync_prev->sink);
1327                 pa_assert(i->sync_prev->sync_next == i);
1328                 i->thread_info.sync_prev->thread_info.sync_next = i;
1329             }
1330
1331             if ((i->thread_info.sync_next = i->sync_next)) {
1332                 pa_assert(i->sink == i->thread_info.sync_next->sink);
1333                 pa_assert(i->sync_next->sync_prev == i);
1334                 i->thread_info.sync_next->thread_info.sync_prev = i;
1335             }
1336
1337             pa_assert(!i->thread_info.attached);
1338             i->thread_info.attached = TRUE;
1339
1340             if (i->attach)
1341                 i->attach(i);
1342
1343             pa_sink_input_set_state_within_thread(i, i->state);
1344
1345             /* The requested latency of the sink input needs to be
1346              * fixed up and then configured on the sink */
1347
1348             if (i->thread_info.requested_sink_latency != (pa_usec_t) -1)
1349                 pa_sink_input_set_requested_latency_within_thread(i, i->thread_info.requested_sink_latency);
1350
1351             pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind);
1352             pa_sink_input_update_max_request(i, s->thread_info.max_request);
1353
1354             /* We don't rewind here automatically. This is left to the
1355              * sink input implementor because some sink inputs need a
1356              * slow start, i.e. need some time to buffer client
1357              * samples before beginning streaming. */
1358
1359             /* In flat volume mode we need to update the volume as
1360              * well */
1361             return o->process_msg(o, PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL);
1362         }
1363
1364         case PA_SINK_MESSAGE_REMOVE_INPUT: {
1365             pa_sink_input *i = PA_SINK_INPUT(userdata);
1366
1367             /* If you change anything here, make sure to change the
1368              * sink input handling a few lines down at
1369              * PA_SINK_MESSAGE_PREPAPRE_MOVE, too. */
1370
1371             if (i->detach)
1372                 i->detach(i);
1373
1374             pa_sink_input_set_state_within_thread(i, i->state);
1375
1376             pa_assert(i->thread_info.attached);
1377             i->thread_info.attached = FALSE;
1378
1379             /* Since the caller sleeps in pa_sink_input_unlink(),
1380              * we can safely access data outside of thread_info even
1381              * though it is mutable */
1382
1383             pa_assert(!i->sync_prev);
1384             pa_assert(!i->sync_next);
1385
1386             if (i->thread_info.sync_prev) {
1387                 i->thread_info.sync_prev->thread_info.sync_next = i->thread_info.sync_prev->sync_next;
1388                 i->thread_info.sync_prev = NULL;
1389             }
1390
1391             if (i->thread_info.sync_next) {
1392                 i->thread_info.sync_next->thread_info.sync_prev = i->thread_info.sync_next->sync_prev;
1393                 i->thread_info.sync_next = NULL;
1394             }
1395
1396             if (pa_hashmap_remove(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index)))
1397                 pa_sink_input_unref(i);
1398
1399             pa_sink_invalidate_requested_latency(s);
1400             pa_sink_request_rewind(s, (size_t) -1);
1401
1402             /* In flat volume mode we need to update the volume as
1403              * well */
1404             return o->process_msg(o, PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL);
1405         }
1406
1407         case PA_SINK_MESSAGE_START_MOVE: {
1408             pa_sink_input *i = PA_SINK_INPUT(userdata);
1409
1410             /* We don't support moving synchronized streams. */
1411             pa_assert(!i->sync_prev);
1412             pa_assert(!i->sync_next);
1413             pa_assert(!i->thread_info.sync_next);
1414             pa_assert(!i->thread_info.sync_prev);
1415
1416             if (i->thread_info.state != PA_SINK_INPUT_CORKED) {
1417                 pa_usec_t usec = 0;
1418                 size_t sink_nbytes, total_nbytes;
1419
1420                 /* Get the latency of the sink */
1421                 if (!(s->flags & PA_SINK_LATENCY) ||
1422                     PA_MSGOBJECT(s)->process_msg(PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
1423                     usec = 0;
1424
1425                 sink_nbytes = pa_usec_to_bytes(usec, &s->sample_spec);
1426                 total_nbytes = sink_nbytes + pa_memblockq_get_length(i->thread_info.render_memblockq);
1427
1428                 if (total_nbytes > 0) {
1429                     i->thread_info.rewrite_nbytes = i->thread_info.resampler ? pa_resampler_request(i->thread_info.resampler, total_nbytes) : total_nbytes;
1430                     i->thread_info.rewrite_flush = TRUE;
1431                     pa_sink_input_process_rewind(i, sink_nbytes);
1432                 }
1433             }
1434
1435             if (i->detach)
1436                 i->detach(i);
1437
1438             pa_assert(i->thread_info.attached);
1439             i->thread_info.attached = FALSE;
1440
1441             /* Let's remove the sink input ...*/
1442             if (pa_hashmap_remove(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index)))
1443                 pa_sink_input_unref(i);
1444
1445             pa_sink_invalidate_requested_latency(s);
1446
1447             pa_log_debug("Requesting rewind due to started move");
1448             pa_sink_request_rewind(s, (size_t) -1);
1449
1450             /* In flat volume mode we need to update the volume as
1451              * well */
1452             return o->process_msg(o, PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL);
1453         }
1454
1455         case PA_SINK_MESSAGE_FINISH_MOVE: {
1456             pa_sink_input *i = PA_SINK_INPUT(userdata);
1457
1458             /* We don't support moving synchronized streams. */
1459             pa_assert(!i->sync_prev);
1460             pa_assert(!i->sync_next);
1461             pa_assert(!i->thread_info.sync_next);
1462             pa_assert(!i->thread_info.sync_prev);
1463
1464             pa_hashmap_put(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index), pa_sink_input_ref(i));
1465
1466             pa_assert(!i->thread_info.attached);
1467             i->thread_info.attached = TRUE;
1468
1469             if (i->attach)
1470                 i->attach(i);
1471
1472             if (i->thread_info.requested_sink_latency != (pa_usec_t) -1)
1473                 pa_sink_input_set_requested_latency_within_thread(i, i->thread_info.requested_sink_latency);
1474
1475             pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind);
1476             pa_sink_input_update_max_request(i, s->thread_info.max_request);
1477
1478             if (i->thread_info.state != PA_SINK_INPUT_CORKED) {
1479                 pa_usec_t usec = 0;
1480                 size_t nbytes;
1481
1482                 /* Get the latency of the sink */
1483                 if (!(s->flags & PA_SINK_LATENCY) ||
1484                     PA_MSGOBJECT(s)->process_msg(PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
1485                     usec = 0;
1486
1487                 nbytes = pa_usec_to_bytes(usec, &s->sample_spec);
1488
1489                 if (nbytes > 0)
1490                     pa_sink_input_drop(i, nbytes);
1491
1492                 pa_log_debug("Requesting rewind due to finished move");
1493                 pa_sink_request_rewind(s, nbytes);
1494             }
1495
1496             /* In flat volume mode we need to update the volume as
1497              * well */
1498             return o->process_msg(o, PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL);
1499         }
1500
1501         case PA_SINK_MESSAGE_SET_VOLUME:
1502
1503             if (!pa_cvolume_equal(&s->thread_info.soft_volume, &s->soft_volume)) {
1504                 s->thread_info.soft_volume = s->soft_volume;
1505                 pa_sink_request_rewind(s, (size_t) -1);
1506             }
1507
1508             if (s->flags & PA_SINK_FLAT_VOLUME)
1509                 sync_input_volumes_within_thread(s);
1510
1511             return 0;
1512
1513         case PA_SINK_MESSAGE_GET_VOLUME:
1514             return 0;
1515
1516         case PA_SINK_MESSAGE_SET_MUTE:
1517
1518             if (s->thread_info.soft_muted != s->muted) {
1519                 s->thread_info.soft_muted = s->muted;
1520                 pa_sink_request_rewind(s, (size_t) -1);
1521             }
1522
1523             return 0;
1524
1525         case PA_SINK_MESSAGE_GET_MUTE:
1526             return 0;
1527
1528         case PA_SINK_MESSAGE_SET_STATE:
1529
1530             s->thread_info.state = PA_PTR_TO_UINT(userdata);
1531
1532             if (s->thread_info.state == PA_SINK_SUSPENDED)
1533                 s->thread_info.rewind_requested = FALSE;
1534
1535             return 0;
1536
1537         case PA_SINK_MESSAGE_DETACH:
1538
1539             /* Detach all streams */
1540             pa_sink_detach_within_thread(s);
1541             return 0;
1542
1543         case PA_SINK_MESSAGE_ATTACH:
1544
1545             /* Reattach all streams */
1546             pa_sink_attach_within_thread(s);
1547             return 0;
1548
1549         case PA_SINK_MESSAGE_GET_REQUESTED_LATENCY: {
1550
1551             pa_usec_t *usec = userdata;
1552             *usec = pa_sink_get_requested_latency_within_thread(s);
1553
1554             if (*usec == (pa_usec_t) -1)
1555                 *usec = s->thread_info.max_latency;
1556
1557             return 0;
1558         }
1559
1560         case PA_SINK_MESSAGE_SET_LATENCY_RANGE: {
1561             pa_usec_t *r = userdata;
1562
1563             pa_sink_update_latency_range(s, r[0], r[1]);
1564
1565             return 0;
1566         }
1567
1568         case PA_SINK_MESSAGE_GET_LATENCY_RANGE: {
1569             pa_usec_t *r = userdata;
1570
1571             r[0] = s->thread_info.min_latency;
1572             r[1] = s->thread_info.max_latency;
1573
1574             return 0;
1575         }
1576
1577         case PA_SINK_MESSAGE_GET_MAX_REWIND:
1578
1579             *((size_t*) userdata) = s->thread_info.max_rewind;
1580             return 0;
1581
1582         case PA_SINK_MESSAGE_GET_MAX_REQUEST:
1583
1584             *((size_t*) userdata) = s->thread_info.max_request;
1585             return 0;
1586
1587         case PA_SINK_MESSAGE_GET_LATENCY:
1588         case PA_SINK_MESSAGE_MAX:
1589             ;
1590     }
1591
1592     return -1;
1593 }
1594
1595 /* Called from main thread */
1596 int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend) {
1597     pa_sink *sink;
1598     uint32_t idx;
1599     int ret = 0;
1600
1601     pa_core_assert_ref(c);
1602
1603     for (sink = PA_SINK(pa_idxset_first(c->sinks, &idx)); sink; sink = PA_SINK(pa_idxset_next(c->sinks, &idx)))
1604         ret -= pa_sink_suspend(sink, suspend) < 0;
1605
1606     return ret;
1607 }
1608
1609 /* Called from main thread */
1610 void pa_sink_detach(pa_sink *s) {
1611     pa_sink_assert_ref(s);
1612     pa_assert(PA_SINK_IS_LINKED(s->state));
1613
1614     pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_DETACH, NULL, 0, NULL) == 0);
1615 }
1616
1617 /* Called from main thread */
1618 void pa_sink_attach(pa_sink *s) {
1619     pa_sink_assert_ref(s);
1620     pa_assert(PA_SINK_IS_LINKED(s->state));
1621
1622     pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_ATTACH, NULL, 0, NULL) == 0);
1623 }
1624
1625 /* Called from IO thread */
1626 void pa_sink_detach_within_thread(pa_sink *s) {
1627     pa_sink_input *i;
1628     void *state = NULL;
1629
1630     pa_sink_assert_ref(s);
1631     pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
1632
1633     while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
1634         if (i->detach)
1635             i->detach(i);
1636
1637     if (s->monitor_source)
1638         pa_source_detach_within_thread(s->monitor_source);
1639 }
1640
1641 /* Called from IO thread */
1642 void pa_sink_attach_within_thread(pa_sink *s) {
1643     pa_sink_input *i;
1644     void *state = NULL;
1645
1646     pa_sink_assert_ref(s);
1647     pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
1648
1649     while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
1650         if (i->attach)
1651             i->attach(i);
1652
1653     if (s->monitor_source)
1654         pa_source_attach_within_thread(s->monitor_source);
1655 }
1656
1657 /* Called from IO thread */
1658 void pa_sink_request_rewind(pa_sink*s, size_t nbytes) {
1659     pa_sink_assert_ref(s);
1660     pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
1661
1662     if (s->thread_info.state == PA_SINK_SUSPENDED)
1663         return;
1664
1665     if (nbytes == (size_t) -1)
1666         nbytes = s->thread_info.max_rewind;
1667
1668     nbytes = PA_MIN(nbytes, s->thread_info.max_rewind);
1669
1670     if (s->thread_info.rewind_requested &&
1671         nbytes <= s->thread_info.rewind_nbytes)
1672         return;
1673
1674     s->thread_info.rewind_nbytes = nbytes;
1675     s->thread_info.rewind_requested = TRUE;
1676
1677     if (s->request_rewind)
1678         s->request_rewind(s);
1679 }
1680
1681 /* Called from IO thread */
1682 pa_usec_t pa_sink_get_requested_latency_within_thread(pa_sink *s) {
1683     pa_usec_t result = (pa_usec_t) -1;
1684     pa_sink_input *i;
1685     void *state = NULL;
1686     pa_usec_t monitor_latency;
1687
1688     pa_sink_assert_ref(s);
1689
1690     if (s->thread_info.requested_latency_valid)
1691         return s->thread_info.requested_latency;
1692
1693     while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
1694
1695         if (i->thread_info.requested_sink_latency != (pa_usec_t) -1 &&
1696             (result == (pa_usec_t) -1 || result > i->thread_info.requested_sink_latency))
1697             result = i->thread_info.requested_sink_latency;
1698
1699     monitor_latency = pa_source_get_requested_latency_within_thread(s->monitor_source);
1700
1701     if (monitor_latency != (pa_usec_t) -1 &&
1702         (result == (pa_usec_t) -1 || result > monitor_latency))
1703         result = monitor_latency;
1704
1705     if (result != (pa_usec_t) -1) {
1706         if (s->thread_info.max_latency > 0 && result > s->thread_info.max_latency)
1707             result = s->thread_info.max_latency;
1708
1709         if (s->thread_info.min_latency > 0 && result < s->thread_info.min_latency)
1710             result = s->thread_info.min_latency;
1711     }
1712
1713     s->thread_info.requested_latency = result;
1714     s->thread_info.requested_latency_valid = TRUE;
1715
1716     return result;
1717 }
1718
1719 /* Called from main thread */
1720 pa_usec_t pa_sink_get_requested_latency(pa_sink *s) {
1721     pa_usec_t usec = 0;
1722
1723     pa_sink_assert_ref(s);
1724     pa_assert(PA_SINK_IS_LINKED(s->state));
1725
1726     if (s->state == PA_SINK_SUSPENDED)
1727         return 0;
1728
1729     pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
1730     return usec;
1731 }
1732
1733 /* Called from IO thread */
1734 void pa_sink_set_max_rewind(pa_sink *s, size_t max_rewind) {
1735     pa_sink_input *i;
1736     void *state = NULL;
1737
1738     pa_sink_assert_ref(s);
1739
1740     if (max_rewind == s->thread_info.max_rewind)
1741         return;
1742
1743     s->thread_info.max_rewind = max_rewind;
1744
1745     if (PA_SINK_IS_LINKED(s->thread_info.state)) {
1746         while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
1747             pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind);
1748     }
1749
1750     if (s->monitor_source)
1751         pa_source_set_max_rewind(s->monitor_source, s->thread_info.max_rewind);
1752 }
1753
1754 /* Called from IO thread */
1755 void pa_sink_set_max_request(pa_sink *s, size_t max_request) {
1756     void *state = NULL;
1757
1758     pa_sink_assert_ref(s);
1759
1760     if (max_request == s->thread_info.max_request)
1761         return;
1762
1763     s->thread_info.max_request = max_request;
1764
1765     if (PA_SINK_IS_LINKED(s->thread_info.state)) {
1766         pa_sink_input *i;
1767
1768         while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
1769             pa_sink_input_update_max_request(i, s->thread_info.max_request);
1770     }
1771 }
1772
1773 /* Called from IO thread */
1774 void pa_sink_invalidate_requested_latency(pa_sink *s) {
1775     pa_sink_input *i;
1776     void *state = NULL;
1777
1778     pa_sink_assert_ref(s);
1779
1780     s->thread_info.requested_latency_valid = FALSE;
1781
1782     if (s->update_requested_latency)
1783         s->update_requested_latency(s);
1784
1785     while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
1786         if (i->update_sink_requested_latency)
1787             i->update_sink_requested_latency(i);
1788 }
1789
1790 /* Called from main thread */
1791 void pa_sink_set_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency) {
1792     pa_sink_assert_ref(s);
1793
1794     /* min_latency == 0:           no limit
1795      * min_latency == (size_t) -1: default limit
1796      * min_latency anything else:  specified limit
1797      *
1798      * Similar for max_latency */
1799
1800     if (min_latency == (pa_usec_t) -1)
1801         min_latency = DEFAULT_MIN_LATENCY;
1802
1803     if (max_latency == (pa_usec_t) -1)
1804         max_latency = min_latency;
1805
1806     pa_assert(!min_latency || !max_latency ||
1807               min_latency <= max_latency);
1808
1809     if (PA_SINK_IS_LINKED(s->state)) {
1810         pa_usec_t r[2];
1811
1812         r[0] = min_latency;
1813         r[1] = max_latency;
1814
1815         pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_LATENCY_RANGE, r, 0, NULL) == 0);
1816     } else {
1817         s->thread_info.min_latency = min_latency;
1818         s->thread_info.max_latency = max_latency;
1819
1820         s->monitor_source->thread_info.min_latency = min_latency;
1821         s->monitor_source->thread_info.max_latency = max_latency;
1822
1823         s->thread_info.requested_latency_valid = s->monitor_source->thread_info.requested_latency_valid = FALSE;
1824     }
1825 }
1826
1827 /* Called from main thread */
1828 void pa_sink_get_latency_range(pa_sink *s, pa_usec_t *min_latency, pa_usec_t *max_latency) {
1829    pa_sink_assert_ref(s);
1830    pa_assert(min_latency);
1831    pa_assert(max_latency);
1832
1833    if (PA_SINK_IS_LINKED(s->state)) {
1834        pa_usec_t r[2] = { 0, 0 };
1835
1836        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY_RANGE, r, 0, NULL) == 0);
1837
1838        *min_latency = r[0];
1839        *max_latency = r[1];
1840    } else {
1841        *min_latency = s->thread_info.min_latency;
1842        *max_latency = s->thread_info.max_latency;
1843    }
1844 }
1845
1846 /* Called from IO thread */
1847 void pa_sink_update_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency) {
1848     pa_sink_input *i;
1849     void *state = NULL;
1850
1851     pa_sink_assert_ref(s);
1852
1853     pa_assert(!min_latency || !max_latency ||
1854               min_latency <= max_latency);
1855
1856     s->thread_info.min_latency = min_latency;
1857     s->thread_info.max_latency = max_latency;
1858
1859     while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
1860         if (i->update_sink_latency_range)
1861             i->update_sink_latency_range(i);
1862
1863     pa_sink_invalidate_requested_latency(s);
1864
1865     pa_source_update_latency_range(s->monitor_source, min_latency, max_latency);
1866 }
1867
1868 /* Called from main context */
1869 size_t pa_sink_get_max_rewind(pa_sink *s) {
1870     size_t r;
1871     pa_sink_assert_ref(s);
1872
1873     if (!PA_SINK_IS_LINKED(s->state))
1874         return s->thread_info.max_rewind;
1875
1876     pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MAX_REWIND, &r, 0, NULL) == 0);
1877
1878     return r;
1879 }
1880
1881 /* Called from main context */
1882 size_t pa_sink_get_max_request(pa_sink *s) {
1883     size_t r;
1884     pa_sink_assert_ref(s);
1885
1886     if (!PA_SINK_IS_LINKED(s->state))
1887         return s->thread_info.max_request;
1888
1889     pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MAX_REQUEST, &r, 0, NULL) == 0);
1890
1891     return r;
1892 }
1893
1894 /* Called from main context */
1895 pa_bool_t pa_device_init_icon(pa_proplist *p, pa_bool_t is_sink) {
1896     const char *ff, *t = NULL, *s = "", *profile, *bus;
1897
1898     pa_assert(p);
1899
1900     if (pa_proplist_contains(p, PA_PROP_DEVICE_ICON_NAME))
1901         return TRUE;
1902
1903     if ((ff = pa_proplist_gets(p, PA_PROP_DEVICE_FORM_FACTOR))) {
1904
1905         if (pa_streq(ff, "microphone"))
1906             t = "audio-input-microphone";
1907         else if (pa_streq(ff, "webcam"))
1908             t = "camera-web";
1909         else if (pa_streq(ff, "computer"))
1910             t = "computer";
1911         else if (pa_streq(ff, "handset"))
1912             t = "phone";
1913     }
1914
1915     if (!t) {
1916         if (is_sink)
1917             t = "audio-card";
1918         else
1919             t = "audio-input-microphone";
1920     }
1921
1922     if ((profile = pa_proplist_gets(p, PA_PROP_DEVICE_PROFILE_NAME))) {
1923         if (strstr(profile, "analog"))
1924             s = "-analog";
1925         else if (strstr(profile, "iec958"))
1926             s = "-iec958";
1927         else if (strstr(profile, "hdmi"))
1928             s = "-hdmi";
1929     }
1930
1931     bus = pa_proplist_gets(p, PA_PROP_DEVICE_BUS);
1932
1933     pa_proplist_setf(p, PA_PROP_DEVICE_ICON_NAME, "%s%s%s%s", t, pa_strempty(s), bus ? "-" : "", pa_strempty(bus));
1934
1935     return TRUE;
1936 }
1937
1938 pa_bool_t pa_device_init_description(pa_proplist *p) {
1939     const char *s;
1940     pa_assert(p);
1941
1942     if (pa_proplist_contains(p, PA_PROP_DEVICE_DESCRIPTION))
1943         return TRUE;
1944
1945     if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_FORM_FACTOR)))
1946         if (pa_streq(s, "internal")) {
1947             pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, _("Internal Audio"));
1948             return TRUE;
1949         }
1950
1951     if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_PRODUCT_NAME))) {
1952         pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, s);
1953         return TRUE;
1954     }
1955
1956     return FALSE;
1957 }