]> code.delx.au - pulseaudio/blob - src/pulsecore/sink.c
make suspend state of monitor source follow the suspend state of the sink it belongs to
[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 ret -= pa_sink_suspend(sink, suspend) < 0;
1610
1611 return ret;
1612 }
1613
1614 /* Called from main thread */
1615 void pa_sink_detach(pa_sink *s) {
1616 pa_sink_assert_ref(s);
1617 pa_assert(PA_SINK_IS_LINKED(s->state));
1618
1619 pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_DETACH, NULL, 0, NULL) == 0);
1620 }
1621
1622 /* Called from main thread */
1623 void pa_sink_attach(pa_sink *s) {
1624 pa_sink_assert_ref(s);
1625 pa_assert(PA_SINK_IS_LINKED(s->state));
1626
1627 pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_ATTACH, NULL, 0, NULL) == 0);
1628 }
1629
1630 /* Called from IO thread */
1631 void pa_sink_detach_within_thread(pa_sink *s) {
1632 pa_sink_input *i;
1633 void *state = NULL;
1634
1635 pa_sink_assert_ref(s);
1636 pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
1637
1638 while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
1639 if (i->detach)
1640 i->detach(i);
1641
1642 if (s->monitor_source)
1643 pa_source_detach_within_thread(s->monitor_source);
1644 }
1645
1646 /* Called from IO thread */
1647 void pa_sink_attach_within_thread(pa_sink *s) {
1648 pa_sink_input *i;
1649 void *state = NULL;
1650
1651 pa_sink_assert_ref(s);
1652 pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
1653
1654 while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
1655 if (i->attach)
1656 i->attach(i);
1657
1658 if (s->monitor_source)
1659 pa_source_attach_within_thread(s->monitor_source);
1660 }
1661
1662 /* Called from IO thread */
1663 void pa_sink_request_rewind(pa_sink*s, size_t nbytes) {
1664 pa_sink_assert_ref(s);
1665 pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
1666
1667 if (s->thread_info.state == PA_SINK_SUSPENDED)
1668 return;
1669
1670 if (nbytes == (size_t) -1)
1671 nbytes = s->thread_info.max_rewind;
1672
1673 nbytes = PA_MIN(nbytes, s->thread_info.max_rewind);
1674
1675 if (s->thread_info.rewind_requested &&
1676 nbytes <= s->thread_info.rewind_nbytes)
1677 return;
1678
1679 s->thread_info.rewind_nbytes = nbytes;
1680 s->thread_info.rewind_requested = TRUE;
1681
1682 if (s->request_rewind)
1683 s->request_rewind(s);
1684 }
1685
1686 /* Called from IO thread */
1687 pa_usec_t pa_sink_get_requested_latency_within_thread(pa_sink *s) {
1688 pa_usec_t result = (pa_usec_t) -1;
1689 pa_sink_input *i;
1690 void *state = NULL;
1691 pa_usec_t monitor_latency;
1692
1693 pa_sink_assert_ref(s);
1694
1695 if (s->thread_info.requested_latency_valid)
1696 return s->thread_info.requested_latency;
1697
1698 while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
1699
1700 if (i->thread_info.requested_sink_latency != (pa_usec_t) -1 &&
1701 (result == (pa_usec_t) -1 || result > i->thread_info.requested_sink_latency))
1702 result = i->thread_info.requested_sink_latency;
1703
1704 monitor_latency = pa_source_get_requested_latency_within_thread(s->monitor_source);
1705
1706 if (monitor_latency != (pa_usec_t) -1 &&
1707 (result == (pa_usec_t) -1 || result > monitor_latency))
1708 result = monitor_latency;
1709
1710 if (result != (pa_usec_t) -1) {
1711 if (s->thread_info.max_latency > 0 && result > s->thread_info.max_latency)
1712 result = s->thread_info.max_latency;
1713
1714 if (s->thread_info.min_latency > 0 && result < s->thread_info.min_latency)
1715 result = s->thread_info.min_latency;
1716 }
1717
1718 s->thread_info.requested_latency = result;
1719 s->thread_info.requested_latency_valid = TRUE;
1720
1721 return result;
1722 }
1723
1724 /* Called from main thread */
1725 pa_usec_t pa_sink_get_requested_latency(pa_sink *s) {
1726 pa_usec_t usec = 0;
1727
1728 pa_sink_assert_ref(s);
1729 pa_assert(PA_SINK_IS_LINKED(s->state));
1730
1731 if (s->state == PA_SINK_SUSPENDED)
1732 return 0;
1733
1734 pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
1735 return usec;
1736 }
1737
1738 /* Called from IO as well as the main thread -- the latter only before the IO thread started up */
1739 void pa_sink_set_max_rewind(pa_sink *s, size_t max_rewind) {
1740 pa_sink_input *i;
1741 void *state = NULL;
1742
1743 pa_sink_assert_ref(s);
1744
1745 if (max_rewind == s->thread_info.max_rewind)
1746 return;
1747
1748 s->thread_info.max_rewind = max_rewind;
1749
1750 if (PA_SINK_IS_LINKED(s->thread_info.state)) {
1751 while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
1752 pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind);
1753 }
1754
1755 if (s->monitor_source)
1756 pa_source_set_max_rewind(s->monitor_source, s->thread_info.max_rewind);
1757 }
1758
1759 /* Called from IO as well as the main thread -- the latter only before the IO thread started up */
1760 void pa_sink_set_max_request(pa_sink *s, size_t max_request) {
1761 void *state = NULL;
1762
1763 pa_sink_assert_ref(s);
1764
1765 if (max_request == s->thread_info.max_request)
1766 return;
1767
1768 s->thread_info.max_request = max_request;
1769
1770 if (PA_SINK_IS_LINKED(s->thread_info.state)) {
1771 pa_sink_input *i;
1772
1773 while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
1774 pa_sink_input_update_max_request(i, s->thread_info.max_request);
1775 }
1776 }
1777
1778 /* Called from IO thread */
1779 void pa_sink_invalidate_requested_latency(pa_sink *s) {
1780 pa_sink_input *i;
1781 void *state = NULL;
1782
1783 pa_sink_assert_ref(s);
1784
1785 s->thread_info.requested_latency_valid = FALSE;
1786
1787 if (s->update_requested_latency)
1788 s->update_requested_latency(s);
1789
1790 while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
1791 if (i->update_sink_requested_latency)
1792 i->update_sink_requested_latency(i);
1793 }
1794
1795 /* Called from main thread */
1796 void pa_sink_set_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency) {
1797 pa_sink_assert_ref(s);
1798
1799 /* min_latency == 0: no limit
1800 * min_latency == (size_t) -1: default limit
1801 * min_latency anything else: specified limit
1802 *
1803 * Similar for max_latency */
1804
1805 if (min_latency == (pa_usec_t) -1)
1806 min_latency = DEFAULT_MIN_LATENCY;
1807
1808 if (max_latency == (pa_usec_t) -1)
1809 max_latency = min_latency;
1810
1811 pa_assert(!min_latency || !max_latency ||
1812 min_latency <= max_latency);
1813
1814 if (PA_SINK_IS_LINKED(s->state)) {
1815 pa_usec_t r[2];
1816
1817 r[0] = min_latency;
1818 r[1] = max_latency;
1819
1820 pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_LATENCY_RANGE, r, 0, NULL) == 0);
1821 } else {
1822 s->thread_info.min_latency = min_latency;
1823 s->thread_info.max_latency = max_latency;
1824
1825 s->monitor_source->thread_info.min_latency = min_latency;
1826 s->monitor_source->thread_info.max_latency = max_latency;
1827
1828 s->thread_info.requested_latency_valid = s->monitor_source->thread_info.requested_latency_valid = FALSE;
1829 }
1830 }
1831
1832 /* Called from main thread */
1833 void pa_sink_get_latency_range(pa_sink *s, pa_usec_t *min_latency, pa_usec_t *max_latency) {
1834 pa_sink_assert_ref(s);
1835 pa_assert(min_latency);
1836 pa_assert(max_latency);
1837
1838 if (PA_SINK_IS_LINKED(s->state)) {
1839 pa_usec_t r[2] = { 0, 0 };
1840
1841 pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY_RANGE, r, 0, NULL) == 0);
1842
1843 *min_latency = r[0];
1844 *max_latency = r[1];
1845 } else {
1846 *min_latency = s->thread_info.min_latency;
1847 *max_latency = s->thread_info.max_latency;
1848 }
1849 }
1850
1851 /* Called from IO thread */
1852 void pa_sink_update_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency) {
1853 pa_sink_input *i;
1854 void *state = NULL;
1855
1856 pa_sink_assert_ref(s);
1857
1858 pa_assert(!min_latency || !max_latency ||
1859 min_latency <= max_latency);
1860
1861 s->thread_info.min_latency = min_latency;
1862 s->thread_info.max_latency = max_latency;
1863
1864 while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
1865 if (i->update_sink_latency_range)
1866 i->update_sink_latency_range(i);
1867
1868 pa_sink_invalidate_requested_latency(s);
1869
1870 pa_source_update_latency_range(s->monitor_source, min_latency, max_latency);
1871 }
1872
1873 /* Called from main context */
1874 size_t pa_sink_get_max_rewind(pa_sink *s) {
1875 size_t r;
1876 pa_sink_assert_ref(s);
1877
1878 if (!PA_SINK_IS_LINKED(s->state))
1879 return s->thread_info.max_rewind;
1880
1881 pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MAX_REWIND, &r, 0, NULL) == 0);
1882
1883 return r;
1884 }
1885
1886 /* Called from main context */
1887 size_t pa_sink_get_max_request(pa_sink *s) {
1888 size_t r;
1889 pa_sink_assert_ref(s);
1890
1891 if (!PA_SINK_IS_LINKED(s->state))
1892 return s->thread_info.max_request;
1893
1894 pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MAX_REQUEST, &r, 0, NULL) == 0);
1895
1896 return r;
1897 }
1898
1899 /* Called from main context */
1900 pa_bool_t pa_device_init_icon(pa_proplist *p, pa_bool_t is_sink) {
1901 const char *ff, *t = NULL, *s = "", *profile, *bus;
1902
1903 pa_assert(p);
1904
1905 if (pa_proplist_contains(p, PA_PROP_DEVICE_ICON_NAME))
1906 return TRUE;
1907
1908 if ((ff = pa_proplist_gets(p, PA_PROP_DEVICE_FORM_FACTOR))) {
1909
1910 if (pa_streq(ff, "microphone"))
1911 t = "audio-input-microphone";
1912 else if (pa_streq(ff, "webcam"))
1913 t = "camera-web";
1914 else if (pa_streq(ff, "computer"))
1915 t = "computer";
1916 else if (pa_streq(ff, "handset"))
1917 t = "phone";
1918 }
1919
1920 if (!t) {
1921 if (is_sink)
1922 t = "audio-card";
1923 else
1924 t = "audio-input-microphone";
1925 }
1926
1927 if ((profile = pa_proplist_gets(p, PA_PROP_DEVICE_PROFILE_NAME))) {
1928 if (strstr(profile, "analog"))
1929 s = "-analog";
1930 else if (strstr(profile, "iec958"))
1931 s = "-iec958";
1932 else if (strstr(profile, "hdmi"))
1933 s = "-hdmi";
1934 }
1935
1936 bus = pa_proplist_gets(p, PA_PROP_DEVICE_BUS);
1937
1938 pa_proplist_setf(p, PA_PROP_DEVICE_ICON_NAME, "%s%s%s%s", t, pa_strempty(s), bus ? "-" : "", pa_strempty(bus));
1939
1940 return TRUE;
1941 }
1942
1943 pa_bool_t pa_device_init_description(pa_proplist *p) {
1944 const char *s;
1945 pa_assert(p);
1946
1947 if (pa_proplist_contains(p, PA_PROP_DEVICE_DESCRIPTION))
1948 return TRUE;
1949
1950 if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_FORM_FACTOR)))
1951 if (pa_streq(s, "internal")) {
1952 pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, _("Internal Audio"));
1953 return TRUE;
1954 }
1955
1956 if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_PRODUCT_NAME))) {
1957 pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, s);
1958 return TRUE;
1959 }
1960
1961 return FALSE;
1962 }