]> code.delx.au - pulseaudio/blob - src/pulsecore/sink.c
1fec018906d9bec85ac1252211421f8d1428d900
[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 ABSOLUTE_MIN_LATENCY (500)
52 #define ABSOLUTE_MAX_LATENCY (10*PA_USEC_PER_SEC)
53
54 static PA_DEFINE_CHECK_TYPE(pa_sink, pa_msgobject);
55
56 static void sink_free(pa_object *s);
57
58 pa_sink_new_data* pa_sink_new_data_init(pa_sink_new_data *data) {
59 pa_assert(data);
60
61 memset(data, 0, sizeof(*data));
62 data->proplist = pa_proplist_new();
63
64 return data;
65 }
66
67 void pa_sink_new_data_set_name(pa_sink_new_data *data, const char *name) {
68 pa_assert(data);
69
70 pa_xfree(data->name);
71 data->name = pa_xstrdup(name);
72 }
73
74 void pa_sink_new_data_set_sample_spec(pa_sink_new_data *data, const pa_sample_spec *spec) {
75 pa_assert(data);
76
77 if ((data->sample_spec_is_set = !!spec))
78 data->sample_spec = *spec;
79 }
80
81 void pa_sink_new_data_set_channel_map(pa_sink_new_data *data, const pa_channel_map *map) {
82 pa_assert(data);
83
84 if ((data->channel_map_is_set = !!map))
85 data->channel_map = *map;
86 }
87
88 void pa_sink_new_data_set_volume(pa_sink_new_data *data, const pa_cvolume *volume) {
89 pa_assert(data);
90
91 if ((data->volume_is_set = !!volume))
92 data->volume = *volume;
93 }
94
95 void pa_sink_new_data_set_muted(pa_sink_new_data *data, pa_bool_t mute) {
96 pa_assert(data);
97
98 data->muted_is_set = TRUE;
99 data->muted = !!mute;
100 }
101
102 void pa_sink_new_data_done(pa_sink_new_data *data) {
103 pa_assert(data);
104
105 pa_xfree(data->name);
106 pa_proplist_free(data->proplist);
107 }
108
109 /* Called from main context */
110 static void reset_callbacks(pa_sink *s) {
111 pa_assert(s);
112
113 s->set_state = NULL;
114 s->get_volume = NULL;
115 s->set_volume = NULL;
116 s->get_mute = NULL;
117 s->set_mute = NULL;
118 s->request_rewind = NULL;
119 s->update_requested_latency = NULL;
120 }
121
122 /* Called from main context */
123 pa_sink* pa_sink_new(
124 pa_core *core,
125 pa_sink_new_data *data,
126 pa_sink_flags_t flags) {
127
128 pa_sink *s;
129 const char *name;
130 char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
131 pa_source_new_data source_data;
132 const char *dn;
133 char *pt;
134
135 pa_assert(core);
136 pa_assert(data);
137 pa_assert(data->name);
138
139 s = pa_msgobject_new(pa_sink);
140
141 if (!(name = pa_namereg_register(core, data->name, PA_NAMEREG_SINK, s, data->namereg_fail))) {
142 pa_xfree(s);
143 return NULL;
144 }
145
146 pa_sink_new_data_set_name(data, name);
147
148 if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_NEW], data) < 0) {
149 pa_xfree(s);
150 pa_namereg_unregister(core, name);
151 return NULL;
152 }
153
154 pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver));
155 pa_return_null_if_fail(data->name && pa_utf8_valid(data->name) && data->name[0]);
156
157 pa_return_null_if_fail(data->sample_spec_is_set && pa_sample_spec_valid(&data->sample_spec));
158
159 if (!data->channel_map_is_set)
160 pa_return_null_if_fail(pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT));
161
162 pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map));
163 pa_return_null_if_fail(data->channel_map.channels == data->sample_spec.channels);
164
165 if (!data->volume_is_set)
166 pa_cvolume_reset(&data->volume, data->sample_spec.channels);
167
168 pa_return_null_if_fail(pa_cvolume_valid(&data->volume));
169 pa_return_null_if_fail(data->volume.channels == data->sample_spec.channels);
170
171 if (!data->muted_is_set)
172 data->muted = FALSE;
173
174 if (data->card)
175 pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->card->proplist);
176
177 pa_device_init_description(data->proplist);
178 pa_device_init_icon(data->proplist, TRUE);
179
180 if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_FIXATE], data) < 0) {
181 pa_xfree(s);
182 pa_namereg_unregister(core, name);
183 return NULL;
184 }
185
186 s->parent.parent.free = sink_free;
187 s->parent.process_msg = pa_sink_process_msg;
188
189 s->core = core;
190 s->state = PA_SINK_INIT;
191 s->flags = flags;
192 s->name = pa_xstrdup(name);
193 s->proplist = pa_proplist_copy(data->proplist);
194 s->driver = pa_xstrdup(pa_path_get_filename(data->driver));
195 s->module = data->module;
196 s->card = data->card;
197
198 s->sample_spec = data->sample_spec;
199 s->channel_map = data->channel_map;
200
201 s->inputs = pa_idxset_new(NULL, NULL);
202 s->n_corked = 0;
203
204 s->virtual_volume = data->volume;
205 pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels);
206 s->base_volume = PA_VOLUME_NORM;
207 s->n_volume_steps = PA_VOLUME_NORM+1;
208 s->muted = data->muted;
209 s->refresh_volume = s->refresh_muted = FALSE;
210
211 reset_callbacks(s);
212 s->userdata = NULL;
213
214 s->asyncmsgq = NULL;
215 s->rtpoll = NULL;
216
217 pa_silence_memchunk_get(
218 &core->silence_cache,
219 core->mempool,
220 &s->silence,
221 &s->sample_spec,
222 0);
223
224 s->thread_info.inputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
225 s->thread_info.soft_volume = s->soft_volume;
226 s->thread_info.soft_muted = s->muted;
227 s->thread_info.state = s->state;
228 s->thread_info.rewind_nbytes = 0;
229 s->thread_info.rewind_requested = FALSE;
230 s->thread_info.max_rewind = 0;
231 s->thread_info.max_request = 0;
232 s->thread_info.requested_latency_valid = FALSE;
233 s->thread_info.requested_latency = 0;
234 s->thread_info.min_latency = ABSOLUTE_MIN_LATENCY;
235 s->thread_info.max_latency = ABSOLUTE_MAX_LATENCY;
236
237 pa_assert_se(pa_idxset_put(core->sinks, s, &s->index) >= 0);
238
239 if (s->card)
240 pa_assert_se(pa_idxset_put(s->card->sinks, s, NULL) >= 0);
241
242 pt = pa_proplist_to_string_sep(s->proplist, "\n ");
243 pa_log_info("Created sink %u \"%s\" with sample spec %s and channel map %s\n %s",
244 s->index,
245 s->name,
246 pa_sample_spec_snprint(st, sizeof(st), &s->sample_spec),
247 pa_channel_map_snprint(cm, sizeof(cm), &s->channel_map),
248 pt);
249 pa_xfree(pt);
250
251 pa_source_new_data_init(&source_data);
252 pa_source_new_data_set_sample_spec(&source_data, &s->sample_spec);
253 pa_source_new_data_set_channel_map(&source_data, &s->channel_map);
254 source_data.name = pa_sprintf_malloc("%s.monitor", name);
255 source_data.driver = data->driver;
256 source_data.module = data->module;
257 source_data.card = data->card;
258
259 dn = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
260 pa_proplist_setf(source_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Monitor of %s", dn ? dn : s->name);
261 pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_CLASS, "monitor");
262
263 s->monitor_source = pa_source_new(core, &source_data, PA_SOURCE_LATENCY);
264
265 pa_source_new_data_done(&source_data);
266
267 if (!s->monitor_source) {
268 pa_sink_unlink(s);
269 pa_sink_unref(s);
270 return NULL;
271 }
272
273 s->monitor_source->monitor_of = s;
274
275 pa_source_set_latency_range(s->monitor_source, s->thread_info.min_latency, s->thread_info.max_latency);
276 pa_source_set_max_rewind(s->monitor_source, s->thread_info.max_rewind);
277
278 return s;
279 }
280
281 /* Called from main context */
282 static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
283 int ret;
284 pa_bool_t suspend_change;
285 pa_sink_state_t original_state;
286
287 pa_assert(s);
288
289 if (s->state == state)
290 return 0;
291
292 original_state = s->state;
293
294 suspend_change =
295 (original_state == PA_SINK_SUSPENDED && PA_SINK_IS_OPENED(state)) ||
296 (PA_SINK_IS_OPENED(original_state) && state == PA_SINK_SUSPENDED);
297
298 if (s->set_state)
299 if ((ret = s->set_state(s, state)) < 0)
300 return ret;
301
302 if (s->asyncmsgq)
303 if ((ret = pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL)) < 0) {
304
305 if (s->set_state)
306 s->set_state(s, original_state);
307
308 return ret;
309 }
310
311 s->state = state;
312
313 if (state != PA_SINK_UNLINKED) { /* if we enter UNLINKED state pa_sink_unlink() will fire the apropriate events */
314 pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], s);
315 pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
316 }
317
318 if (suspend_change) {
319 pa_sink_input *i;
320 uint32_t idx;
321
322 /* We're suspending or resuming, tell everyone about it */
323
324 for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx)))
325 if (s->state == PA_SINK_SUSPENDED &&
326 (i->flags & PA_SINK_INPUT_FAIL_ON_SUSPEND))
327 pa_sink_input_kill(i);
328 else if (i->suspend)
329 i->suspend(i, state == PA_SINK_SUSPENDED);
330
331 if (s->monitor_source)
332 pa_source_sync_suspend(s->monitor_source);
333 }
334
335 return 0;
336 }
337
338 /* Called from main context */
339 void pa_sink_put(pa_sink* s) {
340 pa_sink_assert_ref(s);
341
342 pa_assert(s->state == PA_SINK_INIT);
343
344 /* The following fields must be initialized properly when calling _put() */
345 pa_assert(s->asyncmsgq);
346 pa_assert(s->rtpoll);
347 pa_assert(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_volume_changed(pa_sink *s, const pa_cvolume *new_volume) {
1139 pa_sink_assert_ref(s);
1140
1141 /* The sink implementor may call this if the volume changed to make sure everyone is notified */
1142
1143 if (pa_cvolume_equal(&s->virtual_volume, new_volume))
1144 return;
1145
1146 s->virtual_volume = *new_volume;
1147 pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
1148 }
1149
1150 /* Called from main thread */
1151 void pa_sink_set_mute(pa_sink *s, pa_bool_t mute) {
1152 pa_bool_t old_muted;
1153
1154 pa_sink_assert_ref(s);
1155 pa_assert(PA_SINK_IS_LINKED(s->state));
1156
1157 old_muted = s->muted;
1158 s->muted = mute;
1159
1160 if (s->set_mute)
1161 s->set_mute(s);
1162
1163 pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0);
1164
1165 if (old_muted != s->muted)
1166 pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
1167 }
1168
1169 /* Called from main thread */
1170 pa_bool_t pa_sink_get_mute(pa_sink *s, pa_bool_t force_refresh) {
1171
1172 pa_sink_assert_ref(s);
1173
1174 if (s->refresh_muted || force_refresh) {
1175 pa_bool_t old_muted = s->muted;
1176
1177 if (s->get_mute)
1178 s->get_mute(s);
1179
1180 pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MUTE, NULL, 0, NULL) == 0);
1181
1182 if (old_muted != s->muted)
1183 pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
1184 }
1185
1186 return s->muted;
1187 }
1188
1189 /* Called from main thread */
1190 void pa_sink_mute_changed(pa_sink *s, pa_bool_t new_muted) {
1191 pa_sink_assert_ref(s);
1192
1193 /* The sink implementor may call this if the volume changed to make sure everyone is notified */
1194
1195 if (s->muted == new_muted)
1196 return;
1197
1198 s->muted = new_muted;
1199 pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
1200 }
1201
1202 /* Called from main thread */
1203 pa_bool_t pa_sink_update_proplist(pa_sink *s, pa_update_mode_t mode, pa_proplist *p) {
1204 pa_sink_assert_ref(s);
1205
1206 if (p)
1207 pa_proplist_update(s->proplist, mode, p);
1208
1209 if (PA_SINK_IS_LINKED(s->state)) {
1210 pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], s);
1211 pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
1212 }
1213
1214 return TRUE;
1215 }
1216
1217 /* Called from main thread */
1218 void pa_sink_set_description(pa_sink *s, const char *description) {
1219 const char *old;
1220 pa_sink_assert_ref(s);
1221
1222 if (!description && !pa_proplist_contains(s->proplist, PA_PROP_DEVICE_DESCRIPTION))
1223 return;
1224
1225 old = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
1226
1227 if (old && description && !strcmp(old, description))
1228 return;
1229
1230 if (description)
1231 pa_proplist_sets(s->proplist, PA_PROP_DEVICE_DESCRIPTION, description);
1232 else
1233 pa_proplist_unset(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
1234
1235 if (s->monitor_source) {
1236 char *n;
1237
1238 n = pa_sprintf_malloc("Monitor Source of %s", description ? description : s->name);
1239 pa_source_set_description(s->monitor_source, n);
1240 pa_xfree(n);
1241 }
1242
1243 if (PA_SINK_IS_LINKED(s->state)) {
1244 pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
1245 pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], s);
1246 }
1247 }
1248
1249 /* Called from main thread */
1250 unsigned pa_sink_linked_by(pa_sink *s) {
1251 unsigned ret;
1252
1253 pa_sink_assert_ref(s);
1254 pa_assert(PA_SINK_IS_LINKED(s->state));
1255
1256 ret = pa_idxset_size(s->inputs);
1257
1258 /* We add in the number of streams connected to us here. Please
1259 * note the asymmmetry to pa_sink_used_by()! */
1260
1261 if (s->monitor_source)
1262 ret += pa_source_linked_by(s->monitor_source);
1263
1264 return ret;
1265 }
1266
1267 /* Called from main thread */
1268 unsigned pa_sink_used_by(pa_sink *s) {
1269 unsigned ret;
1270
1271 pa_sink_assert_ref(s);
1272 pa_assert(PA_SINK_IS_LINKED(s->state));
1273
1274 ret = pa_idxset_size(s->inputs);
1275 pa_assert(ret >= s->n_corked);
1276
1277 /* Streams connected to our monitor source do not matter for
1278 * pa_sink_used_by()!.*/
1279
1280 return ret - s->n_corked;
1281 }
1282
1283 /* Called from main thread */
1284 unsigned pa_sink_check_suspend(pa_sink *s) {
1285 unsigned ret;
1286 pa_sink_input *i;
1287 uint32_t idx;
1288
1289 pa_sink_assert_ref(s);
1290
1291 if (!PA_SINK_IS_LINKED(s->state))
1292 return 0;
1293
1294 ret = 0;
1295
1296 for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) {
1297 pa_sink_input_state_t st;
1298
1299 st = pa_sink_input_get_state(i);
1300 pa_assert(PA_SINK_INPUT_IS_LINKED(st));
1301
1302 if (st == PA_SINK_INPUT_CORKED)
1303 continue;
1304
1305 if (i->flags & PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND)
1306 continue;
1307
1308 ret ++;
1309 }
1310
1311 if (s->monitor_source)
1312 ret += pa_source_check_suspend(s->monitor_source);
1313
1314 return ret;
1315 }
1316
1317 /* Called from the IO thread */
1318 static void sync_input_volumes_within_thread(pa_sink *s) {
1319 pa_sink_input *i;
1320 void *state = NULL;
1321
1322 pa_sink_assert_ref(s);
1323
1324 while ((i = PA_SINK_INPUT(pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))) {
1325 if (pa_cvolume_equal(&i->thread_info.soft_volume, &i->soft_volume))
1326 continue;
1327
1328 i->thread_info.soft_volume = i->soft_volume;
1329 pa_sink_input_request_rewind(i, 0, TRUE, FALSE, FALSE);
1330 }
1331 }
1332
1333 /* Called from IO thread, except when it is not */
1334 int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
1335 pa_sink *s = PA_SINK(o);
1336 pa_sink_assert_ref(s);
1337
1338 switch ((pa_sink_message_t) code) {
1339
1340 case PA_SINK_MESSAGE_ADD_INPUT: {
1341 pa_sink_input *i = PA_SINK_INPUT(userdata);
1342
1343 /* If you change anything here, make sure to change the
1344 * sink input handling a few lines down at
1345 * PA_SINK_MESSAGE_FINISH_MOVE, too. */
1346
1347 pa_hashmap_put(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index), pa_sink_input_ref(i));
1348
1349 /* Since the caller sleeps in pa_sink_input_put(), we can
1350 * safely access data outside of thread_info even though
1351 * it is mutable */
1352
1353 if ((i->thread_info.sync_prev = i->sync_prev)) {
1354 pa_assert(i->sink == i->thread_info.sync_prev->sink);
1355 pa_assert(i->sync_prev->sync_next == i);
1356 i->thread_info.sync_prev->thread_info.sync_next = i;
1357 }
1358
1359 if ((i->thread_info.sync_next = i->sync_next)) {
1360 pa_assert(i->sink == i->thread_info.sync_next->sink);
1361 pa_assert(i->sync_next->sync_prev == i);
1362 i->thread_info.sync_next->thread_info.sync_prev = i;
1363 }
1364
1365 pa_assert(!i->thread_info.attached);
1366 i->thread_info.attached = TRUE;
1367
1368 if (i->attach)
1369 i->attach(i);
1370
1371 pa_sink_input_set_state_within_thread(i, i->state);
1372
1373 /* The requested latency of the sink input needs to be
1374 * fixed up and then configured on the sink */
1375
1376 if (i->thread_info.requested_sink_latency != (pa_usec_t) -1)
1377 pa_sink_input_set_requested_latency_within_thread(i, i->thread_info.requested_sink_latency);
1378
1379 pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind);
1380 pa_sink_input_update_max_request(i, s->thread_info.max_request);
1381
1382 /* We don't rewind here automatically. This is left to the
1383 * sink input implementor because some sink inputs need a
1384 * slow start, i.e. need some time to buffer client
1385 * samples before beginning streaming. */
1386
1387 /* In flat volume mode we need to update the volume as
1388 * well */
1389 return o->process_msg(o, PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL);
1390 }
1391
1392 case PA_SINK_MESSAGE_REMOVE_INPUT: {
1393 pa_sink_input *i = PA_SINK_INPUT(userdata);
1394
1395 /* If you change anything here, make sure to change the
1396 * sink input handling a few lines down at
1397 * PA_SINK_MESSAGE_PREPAPRE_MOVE, too. */
1398
1399 if (i->detach)
1400 i->detach(i);
1401
1402 pa_sink_input_set_state_within_thread(i, i->state);
1403
1404 pa_assert(i->thread_info.attached);
1405 i->thread_info.attached = FALSE;
1406
1407 /* Since the caller sleeps in pa_sink_input_unlink(),
1408 * we can safely access data outside of thread_info even
1409 * though it is mutable */
1410
1411 pa_assert(!i->sync_prev);
1412 pa_assert(!i->sync_next);
1413
1414 if (i->thread_info.sync_prev) {
1415 i->thread_info.sync_prev->thread_info.sync_next = i->thread_info.sync_prev->sync_next;
1416 i->thread_info.sync_prev = NULL;
1417 }
1418
1419 if (i->thread_info.sync_next) {
1420 i->thread_info.sync_next->thread_info.sync_prev = i->thread_info.sync_next->sync_prev;
1421 i->thread_info.sync_next = NULL;
1422 }
1423
1424 if (pa_hashmap_remove(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index)))
1425 pa_sink_input_unref(i);
1426
1427 pa_sink_invalidate_requested_latency(s);
1428 pa_sink_request_rewind(s, (size_t) -1);
1429
1430 /* In flat volume mode we need to update the volume as
1431 * well */
1432 return o->process_msg(o, PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL);
1433 }
1434
1435 case PA_SINK_MESSAGE_START_MOVE: {
1436 pa_sink_input *i = PA_SINK_INPUT(userdata);
1437
1438 /* We don't support moving synchronized streams. */
1439 pa_assert(!i->sync_prev);
1440 pa_assert(!i->sync_next);
1441 pa_assert(!i->thread_info.sync_next);
1442 pa_assert(!i->thread_info.sync_prev);
1443
1444 if (i->thread_info.state != PA_SINK_INPUT_CORKED) {
1445 pa_usec_t usec = 0;
1446 size_t sink_nbytes, total_nbytes;
1447
1448 /* Get the latency of the sink */
1449 if (!(s->flags & PA_SINK_LATENCY) ||
1450 PA_MSGOBJECT(s)->process_msg(PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
1451 usec = 0;
1452
1453 sink_nbytes = pa_usec_to_bytes(usec, &s->sample_spec);
1454 total_nbytes = sink_nbytes + pa_memblockq_get_length(i->thread_info.render_memblockq);
1455
1456 if (total_nbytes > 0) {
1457 i->thread_info.rewrite_nbytes = i->thread_info.resampler ? pa_resampler_request(i->thread_info.resampler, total_nbytes) : total_nbytes;
1458 i->thread_info.rewrite_flush = TRUE;
1459 pa_sink_input_process_rewind(i, sink_nbytes);
1460 }
1461 }
1462
1463 if (i->detach)
1464 i->detach(i);
1465
1466 pa_assert(i->thread_info.attached);
1467 i->thread_info.attached = FALSE;
1468
1469 /* Let's remove the sink input ...*/
1470 if (pa_hashmap_remove(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index)))
1471 pa_sink_input_unref(i);
1472
1473 pa_sink_invalidate_requested_latency(s);
1474
1475 pa_log_debug("Requesting rewind due to started move");
1476 pa_sink_request_rewind(s, (size_t) -1);
1477
1478 /* In flat volume mode we need to update the volume as
1479 * well */
1480 return o->process_msg(o, PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL);
1481 }
1482
1483 case PA_SINK_MESSAGE_FINISH_MOVE: {
1484 pa_sink_input *i = PA_SINK_INPUT(userdata);
1485
1486 /* We don't support moving synchronized streams. */
1487 pa_assert(!i->sync_prev);
1488 pa_assert(!i->sync_next);
1489 pa_assert(!i->thread_info.sync_next);
1490 pa_assert(!i->thread_info.sync_prev);
1491
1492 pa_hashmap_put(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index), pa_sink_input_ref(i));
1493
1494 pa_assert(!i->thread_info.attached);
1495 i->thread_info.attached = TRUE;
1496
1497 if (i->attach)
1498 i->attach(i);
1499
1500 if (i->thread_info.requested_sink_latency != (pa_usec_t) -1)
1501 pa_sink_input_set_requested_latency_within_thread(i, i->thread_info.requested_sink_latency);
1502
1503 pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind);
1504 pa_sink_input_update_max_request(i, s->thread_info.max_request);
1505
1506 if (i->thread_info.state != PA_SINK_INPUT_CORKED) {
1507 pa_usec_t usec = 0;
1508 size_t nbytes;
1509
1510 /* Get the latency of the sink */
1511 if (!(s->flags & PA_SINK_LATENCY) ||
1512 PA_MSGOBJECT(s)->process_msg(PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
1513 usec = 0;
1514
1515 nbytes = pa_usec_to_bytes(usec, &s->sample_spec);
1516
1517 if (nbytes > 0)
1518 pa_sink_input_drop(i, nbytes);
1519
1520 pa_log_debug("Requesting rewind due to finished move");
1521 pa_sink_request_rewind(s, nbytes);
1522 }
1523
1524 /* In flat volume mode we need to update the volume as
1525 * well */
1526 return o->process_msg(o, PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL);
1527 }
1528
1529 case PA_SINK_MESSAGE_SET_VOLUME:
1530
1531 if (!pa_cvolume_equal(&s->thread_info.soft_volume, &s->soft_volume)) {
1532 s->thread_info.soft_volume = s->soft_volume;
1533 pa_sink_request_rewind(s, (size_t) -1);
1534 }
1535
1536 if (s->flags & PA_SINK_FLAT_VOLUME)
1537 sync_input_volumes_within_thread(s);
1538
1539 return 0;
1540
1541 case PA_SINK_MESSAGE_GET_VOLUME:
1542 return 0;
1543
1544 case PA_SINK_MESSAGE_SET_MUTE:
1545
1546 if (s->thread_info.soft_muted != s->muted) {
1547 s->thread_info.soft_muted = s->muted;
1548 pa_sink_request_rewind(s, (size_t) -1);
1549 }
1550
1551 return 0;
1552
1553 case PA_SINK_MESSAGE_GET_MUTE:
1554 return 0;
1555
1556 case PA_SINK_MESSAGE_SET_STATE:
1557
1558 s->thread_info.state = PA_PTR_TO_UINT(userdata);
1559
1560 if (s->thread_info.state == PA_SINK_SUSPENDED) {
1561 s->thread_info.rewind_nbytes = 0;
1562 s->thread_info.rewind_requested = FALSE;
1563 }
1564
1565 return 0;
1566
1567 case PA_SINK_MESSAGE_DETACH:
1568
1569 /* Detach all streams */
1570 pa_sink_detach_within_thread(s);
1571 return 0;
1572
1573 case PA_SINK_MESSAGE_ATTACH:
1574
1575 /* Reattach all streams */
1576 pa_sink_attach_within_thread(s);
1577 return 0;
1578
1579 case PA_SINK_MESSAGE_GET_REQUESTED_LATENCY: {
1580
1581 pa_usec_t *usec = userdata;
1582 *usec = pa_sink_get_requested_latency_within_thread(s);
1583
1584 if (*usec == (pa_usec_t) -1)
1585 *usec = s->thread_info.max_latency;
1586
1587 return 0;
1588 }
1589
1590 case PA_SINK_MESSAGE_SET_LATENCY_RANGE: {
1591 pa_usec_t *r = userdata;
1592
1593 pa_sink_set_latency_range_within_thread(s, r[0], r[1]);
1594
1595 return 0;
1596 }
1597
1598 case PA_SINK_MESSAGE_GET_LATENCY_RANGE: {
1599 pa_usec_t *r = userdata;
1600
1601 r[0] = s->thread_info.min_latency;
1602 r[1] = s->thread_info.max_latency;
1603
1604 return 0;
1605 }
1606
1607 case PA_SINK_MESSAGE_GET_MAX_REWIND:
1608
1609 *((size_t*) userdata) = s->thread_info.max_rewind;
1610 return 0;
1611
1612 case PA_SINK_MESSAGE_GET_MAX_REQUEST:
1613
1614 *((size_t*) userdata) = s->thread_info.max_request;
1615 return 0;
1616
1617 case PA_SINK_MESSAGE_SET_MAX_REWIND:
1618
1619 pa_sink_set_max_rewind_within_thread(s, (size_t) offset);
1620 return 0;
1621
1622 case PA_SINK_MESSAGE_SET_MAX_REQUEST:
1623
1624 pa_sink_set_max_request_within_thread(s, (size_t) offset);
1625 return 0;
1626
1627 case PA_SINK_MESSAGE_GET_LATENCY:
1628 case PA_SINK_MESSAGE_MAX:
1629 ;
1630 }
1631
1632 return -1;
1633 }
1634
1635 /* Called from main thread */
1636 int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend) {
1637 pa_sink *sink;
1638 uint32_t idx;
1639 int ret = 0;
1640
1641 pa_core_assert_ref(c);
1642
1643 for (sink = PA_SINK(pa_idxset_first(c->sinks, &idx)); sink; sink = PA_SINK(pa_idxset_next(c->sinks, &idx))) {
1644 int r;
1645
1646 if ((r = pa_sink_suspend(sink, suspend)) < 0)
1647 ret = r;
1648 }
1649
1650 return ret;
1651 }
1652
1653 /* Called from main thread */
1654 void pa_sink_detach(pa_sink *s) {
1655 pa_sink_assert_ref(s);
1656 pa_assert(PA_SINK_IS_LINKED(s->state));
1657
1658 pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_DETACH, NULL, 0, NULL) == 0);
1659 }
1660
1661 /* Called from main thread */
1662 void pa_sink_attach(pa_sink *s) {
1663 pa_sink_assert_ref(s);
1664 pa_assert(PA_SINK_IS_LINKED(s->state));
1665
1666 pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_ATTACH, NULL, 0, NULL) == 0);
1667 }
1668
1669 /* Called from IO thread */
1670 void pa_sink_detach_within_thread(pa_sink *s) {
1671 pa_sink_input *i;
1672 void *state = NULL;
1673
1674 pa_sink_assert_ref(s);
1675 pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
1676
1677 while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
1678 if (i->detach)
1679 i->detach(i);
1680
1681 if (s->monitor_source)
1682 pa_source_detach_within_thread(s->monitor_source);
1683 }
1684
1685 /* Called from IO thread */
1686 void pa_sink_attach_within_thread(pa_sink *s) {
1687 pa_sink_input *i;
1688 void *state = NULL;
1689
1690 pa_sink_assert_ref(s);
1691 pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
1692
1693 while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
1694 if (i->attach)
1695 i->attach(i);
1696
1697 if (s->monitor_source)
1698 pa_source_attach_within_thread(s->monitor_source);
1699 }
1700
1701 /* Called from IO thread */
1702 void pa_sink_request_rewind(pa_sink*s, size_t nbytes) {
1703 pa_sink_assert_ref(s);
1704 pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
1705
1706 if (s->thread_info.state == PA_SINK_SUSPENDED)
1707 return;
1708
1709 if (nbytes == (size_t) -1)
1710 nbytes = s->thread_info.max_rewind;
1711
1712 nbytes = PA_MIN(nbytes, s->thread_info.max_rewind);
1713
1714 if (s->thread_info.rewind_requested &&
1715 nbytes <= s->thread_info.rewind_nbytes)
1716 return;
1717
1718 s->thread_info.rewind_nbytes = nbytes;
1719 s->thread_info.rewind_requested = TRUE;
1720
1721 if (s->request_rewind)
1722 s->request_rewind(s);
1723 }
1724
1725 /* Called from IO thread */
1726 pa_usec_t pa_sink_get_requested_latency_within_thread(pa_sink *s) {
1727 pa_usec_t result = (pa_usec_t) -1;
1728 pa_sink_input *i;
1729 void *state = NULL;
1730 pa_usec_t monitor_latency;
1731
1732 pa_sink_assert_ref(s);
1733
1734 if (s->thread_info.requested_latency_valid)
1735 return s->thread_info.requested_latency;
1736
1737 while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
1738
1739 if (i->thread_info.requested_sink_latency != (pa_usec_t) -1 &&
1740 (result == (pa_usec_t) -1 || result > i->thread_info.requested_sink_latency))
1741 result = i->thread_info.requested_sink_latency;
1742
1743 monitor_latency = pa_source_get_requested_latency_within_thread(s->monitor_source);
1744
1745 if (monitor_latency != (pa_usec_t) -1 &&
1746 (result == (pa_usec_t) -1 || result > monitor_latency))
1747 result = monitor_latency;
1748
1749 if (result != (pa_usec_t) -1) {
1750 if (result > s->thread_info.max_latency)
1751 result = s->thread_info.max_latency;
1752
1753 if (result < s->thread_info.min_latency)
1754 result = s->thread_info.min_latency;
1755 }
1756
1757 s->thread_info.requested_latency = result;
1758 s->thread_info.requested_latency_valid = TRUE;
1759
1760 return result;
1761 }
1762
1763 /* Called from main thread */
1764 pa_usec_t pa_sink_get_requested_latency(pa_sink *s) {
1765 pa_usec_t usec = 0;
1766
1767 pa_sink_assert_ref(s);
1768 pa_assert(PA_SINK_IS_LINKED(s->state));
1769
1770 if (s->state == PA_SINK_SUSPENDED)
1771 return 0;
1772
1773 pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
1774 return usec;
1775 }
1776
1777 /* Called from IO as well as the main thread -- the latter only before the IO thread started up */
1778 void pa_sink_set_max_rewind_within_thread(pa_sink *s, size_t max_rewind) {
1779 pa_sink_input *i;
1780 void *state = NULL;
1781
1782 pa_sink_assert_ref(s);
1783
1784 if (max_rewind == s->thread_info.max_rewind)
1785 return;
1786
1787 s->thread_info.max_rewind = max_rewind;
1788
1789 if (PA_SINK_IS_LINKED(s->thread_info.state)) {
1790 while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
1791 pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind);
1792 }
1793
1794 if (s->monitor_source)
1795 pa_source_set_max_rewind(s->monitor_source, s->thread_info.max_rewind);
1796 }
1797
1798 /* Called from main thread */
1799 void pa_sink_set_max_rewind(pa_sink *s, size_t max_rewind) {
1800 pa_sink_assert_ref(s);
1801
1802 if (PA_SINK_IS_LINKED(s->state))
1803 pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MAX_REWIND, NULL, max_rewind, NULL) == 0);
1804 else
1805 pa_sink_set_max_rewind_within_thread(s, max_rewind);
1806 }
1807
1808 /* Called from IO as well as the main thread -- the latter only before the IO thread started up */
1809 void pa_sink_set_max_request_within_thread(pa_sink *s, size_t max_request) {
1810 void *state = NULL;
1811
1812 pa_sink_assert_ref(s);
1813
1814 if (max_request == s->thread_info.max_request)
1815 return;
1816
1817 s->thread_info.max_request = max_request;
1818
1819 if (PA_SINK_IS_LINKED(s->thread_info.state)) {
1820 pa_sink_input *i;
1821
1822 while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
1823 pa_sink_input_update_max_request(i, s->thread_info.max_request);
1824 }
1825 }
1826
1827 /* Called from main thread */
1828 void pa_sink_set_max_request(pa_sink *s, size_t max_request) {
1829 pa_sink_assert_ref(s);
1830
1831 if (PA_SINK_IS_LINKED(s->state))
1832 pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MAX_REQUEST, NULL, max_request, NULL) == 0);
1833 else
1834 pa_sink_set_max_request_within_thread(s, max_request);
1835 }
1836
1837 /* Called from IO thread */
1838 void pa_sink_invalidate_requested_latency(pa_sink *s) {
1839 pa_sink_input *i;
1840 void *state = NULL;
1841
1842 pa_sink_assert_ref(s);
1843
1844 s->thread_info.requested_latency_valid = FALSE;
1845
1846 if (s->update_requested_latency)
1847 s->update_requested_latency(s);
1848
1849 while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
1850 if (i->update_sink_requested_latency)
1851 i->update_sink_requested_latency(i);
1852 }
1853
1854 /* Called from main thread */
1855 void pa_sink_set_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency) {
1856 pa_sink_assert_ref(s);
1857
1858 /* min_latency == 0: no limit
1859 * min_latency anything else: specified limit
1860 *
1861 * Similar for max_latency */
1862
1863 if (min_latency < ABSOLUTE_MIN_LATENCY)
1864 min_latency = ABSOLUTE_MIN_LATENCY;
1865
1866 if (max_latency <= 0 ||
1867 max_latency > ABSOLUTE_MAX_LATENCY)
1868 max_latency = ABSOLUTE_MAX_LATENCY;
1869
1870 pa_assert(min_latency <= max_latency);
1871
1872 if (PA_SINK_IS_LINKED(s->state)) {
1873 pa_usec_t r[2];
1874
1875 r[0] = min_latency;
1876 r[1] = max_latency;
1877
1878 pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_LATENCY_RANGE, r, 0, NULL) == 0);
1879 } else {
1880 s->thread_info.min_latency = min_latency;
1881 s->thread_info.max_latency = max_latency;
1882
1883 s->monitor_source->thread_info.min_latency = min_latency;
1884 s->monitor_source->thread_info.max_latency = max_latency;
1885
1886 s->thread_info.requested_latency_valid = s->monitor_source->thread_info.requested_latency_valid = FALSE;
1887 }
1888 }
1889
1890 /* Called from main thread */
1891 void pa_sink_get_latency_range(pa_sink *s, pa_usec_t *min_latency, pa_usec_t *max_latency) {
1892 pa_sink_assert_ref(s);
1893 pa_assert(min_latency);
1894 pa_assert(max_latency);
1895
1896 if (PA_SINK_IS_LINKED(s->state)) {
1897 pa_usec_t r[2] = { 0, 0 };
1898
1899 pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY_RANGE, r, 0, NULL) == 0);
1900
1901 *min_latency = r[0];
1902 *max_latency = r[1];
1903 } else {
1904 *min_latency = s->thread_info.min_latency;
1905 *max_latency = s->thread_info.max_latency;
1906 }
1907 }
1908
1909 /* Called from IO thread */
1910 void pa_sink_set_latency_range_within_thread(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency) {
1911 pa_sink_input *i;
1912 void *state = NULL;
1913
1914 pa_sink_assert_ref(s);
1915
1916 pa_assert(min_latency >= ABSOLUTE_MIN_LATENCY);
1917 pa_assert(max_latency <= ABSOLUTE_MAX_LATENCY);
1918 pa_assert(min_latency <= max_latency);
1919
1920 s->thread_info.min_latency = min_latency;
1921 s->thread_info.max_latency = max_latency;
1922
1923 while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
1924 if (i->update_sink_latency_range)
1925 i->update_sink_latency_range(i);
1926
1927 pa_sink_invalidate_requested_latency(s);
1928
1929 pa_source_set_latency_range_within_thread(s->monitor_source, min_latency, max_latency);
1930 }
1931
1932 /* Called from main context */
1933 size_t pa_sink_get_max_rewind(pa_sink *s) {
1934 size_t r;
1935 pa_sink_assert_ref(s);
1936
1937 if (!PA_SINK_IS_LINKED(s->state))
1938 return s->thread_info.max_rewind;
1939
1940 pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MAX_REWIND, &r, 0, NULL) == 0);
1941
1942 return r;
1943 }
1944
1945 /* Called from main context */
1946 size_t pa_sink_get_max_request(pa_sink *s) {
1947 size_t r;
1948 pa_sink_assert_ref(s);
1949
1950 if (!PA_SINK_IS_LINKED(s->state))
1951 return s->thread_info.max_request;
1952
1953 pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MAX_REQUEST, &r, 0, NULL) == 0);
1954
1955 return r;
1956 }
1957
1958 /* Called from main context */
1959 pa_bool_t pa_device_init_icon(pa_proplist *p, pa_bool_t is_sink) {
1960 const char *ff, *c, *t = NULL, *s = "", *profile, *bus;
1961
1962 pa_assert(p);
1963
1964 if (pa_proplist_contains(p, PA_PROP_DEVICE_ICON_NAME))
1965 return TRUE;
1966
1967 if ((ff = pa_proplist_gets(p, PA_PROP_DEVICE_FORM_FACTOR))) {
1968
1969 if (pa_streq(ff, "microphone"))
1970 t = "audio-input-microphone";
1971 else if (pa_streq(ff, "webcam"))
1972 t = "camera-web";
1973 else if (pa_streq(ff, "computer"))
1974 t = "computer";
1975 else if (pa_streq(ff, "handset"))
1976 t = "phone";
1977 else if (pa_streq(ff, "portable"))
1978 t = "multimedia-player";
1979 else if (pa_streq(ff, "tv"))
1980 t = "video-display";
1981 }
1982
1983 if (!t)
1984 if ((c = pa_proplist_gets(p, PA_PROP_DEVICE_CLASS)))
1985 if (pa_streq(c, "modem"))
1986 t = "modem";
1987
1988 if (!t) {
1989 if (is_sink)
1990 t = "audio-card";
1991 else
1992 t = "audio-input-microphone";
1993 }
1994
1995 if ((profile = pa_proplist_gets(p, PA_PROP_DEVICE_PROFILE_NAME))) {
1996 if (strstr(profile, "analog"))
1997 s = "-analog";
1998 else if (strstr(profile, "iec958"))
1999 s = "-iec958";
2000 else if (strstr(profile, "hdmi"))
2001 s = "-hdmi";
2002 }
2003
2004 bus = pa_proplist_gets(p, PA_PROP_DEVICE_BUS);
2005
2006 pa_proplist_setf(p, PA_PROP_DEVICE_ICON_NAME, "%s%s%s%s", t, pa_strempty(s), bus ? "-" : "", pa_strempty(bus));
2007
2008 return TRUE;
2009 }
2010
2011 pa_bool_t pa_device_init_description(pa_proplist *p) {
2012 const char *s;
2013 pa_assert(p);
2014
2015 if (pa_proplist_contains(p, PA_PROP_DEVICE_DESCRIPTION))
2016 return TRUE;
2017
2018 if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_FORM_FACTOR)))
2019 if (pa_streq(s, "internal")) {
2020 pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, _("Internal Audio"));
2021 return TRUE;
2022 }
2023
2024 if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_CLASS)))
2025 if (pa_streq(s, "modem")) {
2026 pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, _("Modem"));
2027 return TRUE;
2028 }
2029
2030 if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_PRODUCT_NAME))) {
2031 pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, s);
2032 return TRUE;
2033 }
2034
2035 return FALSE;
2036 }