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