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