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