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