2 This file is part of PulseAudio.
4 Copyright 2004-2006 Lennart Poettering
5 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
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.
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.
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
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>
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>
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)
55 static PA_DEFINE_CHECK_TYPE(pa_sink
, pa_msgobject
);
57 static void sink_free(pa_object
*s
);
59 pa_sink_new_data
* pa_sink_new_data_init(pa_sink_new_data
*data
) {
62 memset(data
, 0, sizeof(*data
));
63 data
->proplist
= pa_proplist_new();
68 void pa_sink_new_data_set_name(pa_sink_new_data
*data
, const char *name
) {
72 data
->name
= pa_xstrdup(name
);
75 void pa_sink_new_data_set_sample_spec(pa_sink_new_data
*data
, const pa_sample_spec
*spec
) {
78 if ((data
->sample_spec_is_set
= !!spec
))
79 data
->sample_spec
= *spec
;
82 void pa_sink_new_data_set_channel_map(pa_sink_new_data
*data
, const pa_channel_map
*map
) {
85 if ((data
->channel_map_is_set
= !!map
))
86 data
->channel_map
= *map
;
89 void pa_sink_new_data_set_volume(pa_sink_new_data
*data
, const pa_cvolume
*volume
) {
92 if ((data
->volume_is_set
= !!volume
))
93 data
->volume
= *volume
;
96 void pa_sink_new_data_set_muted(pa_sink_new_data
*data
, pa_bool_t mute
) {
99 data
->muted_is_set
= TRUE
;
100 data
->muted
= !!mute
;
103 void pa_sink_new_data_done(pa_sink_new_data
*data
) {
106 pa_xfree(data
->name
);
107 pa_proplist_free(data
->proplist
);
110 /* Called from main context */
111 static void reset_callbacks(pa_sink
*s
) {
115 s
->get_volume
= NULL
;
116 s
->set_volume
= NULL
;
119 s
->request_rewind
= NULL
;
120 s
->update_requested_latency
= NULL
;
123 /* Called from main context */
124 pa_sink
* pa_sink_new(
126 pa_sink_new_data
*data
,
127 pa_sink_flags_t flags
) {
131 char st
[PA_SAMPLE_SPEC_SNPRINT_MAX
], cm
[PA_CHANNEL_MAP_SNPRINT_MAX
];
132 pa_source_new_data source_data
;
138 pa_assert(data
->name
);
140 s
= pa_msgobject_new(pa_sink
);
142 if (!(name
= pa_namereg_register(core
, data
->name
, PA_NAMEREG_SINK
, s
, data
->namereg_fail
))) {
147 pa_sink_new_data_set_name(data
, name
);
149 if (pa_hook_fire(&core
->hooks
[PA_CORE_HOOK_SINK_NEW
], data
) < 0) {
151 pa_namereg_unregister(core
, name
);
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]);
158 pa_return_null_if_fail(data
->sample_spec_is_set
&& pa_sample_spec_valid(&data
->sample_spec
));
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
));
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
);
166 if (!data
->volume_is_set
)
167 pa_cvolume_reset(&data
->volume
, data
->sample_spec
.channels
);
169 pa_return_null_if_fail(pa_cvolume_valid(&data
->volume
));
170 pa_return_null_if_fail(data
->volume
.channels
== data
->sample_spec
.channels
);
172 if (!data
->muted_is_set
)
176 pa_proplist_update(data
->proplist
, PA_UPDATE_MERGE
, data
->card
->proplist
);
178 pa_device_init_description(data
->proplist
);
179 pa_device_init_icon(data
->proplist
, TRUE
);
181 if (pa_hook_fire(&core
->hooks
[PA_CORE_HOOK_SINK_FIXATE
], data
) < 0) {
183 pa_namereg_unregister(core
, name
);
187 s
->parent
.parent
.free
= sink_free
;
188 s
->parent
.process_msg
= pa_sink_process_msg
;
191 s
->state
= PA_SINK_INIT
;
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
;
199 s
->sample_spec
= data
->sample_spec
;
200 s
->channel_map
= data
->channel_map
;
202 s
->inputs
= pa_idxset_new(NULL
, NULL
);
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
;
212 s
->fixed_latency
= flags
& PA_SINK_DYNAMIC_LATENCY
? 0 : DEFAULT_FIXED_LATENCY
;
220 pa_silence_memchunk_get(
221 &core
->silence_cache
,
227 s
->thread_info
.inputs
= pa_hashmap_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
228 s
->thread_info
.soft_volume
= s
->soft_volume
;
229 s
->thread_info
.soft_muted
= s
->muted
;
230 s
->thread_info
.state
= s
->state
;
231 s
->thread_info
.rewind_nbytes
= 0;
232 s
->thread_info
.rewind_requested
= FALSE
;
233 s
->thread_info
.max_rewind
= 0;
234 s
->thread_info
.max_request
= 0;
235 s
->thread_info
.requested_latency_valid
= FALSE
;
236 s
->thread_info
.requested_latency
= 0;
237 s
->thread_info
.min_latency
= ABSOLUTE_MIN_LATENCY
;
238 s
->thread_info
.max_latency
= ABSOLUTE_MAX_LATENCY
;
240 pa_assert_se(pa_idxset_put(core
->sinks
, s
, &s
->index
) >= 0);
243 pa_assert_se(pa_idxset_put(s
->card
->sinks
, s
, NULL
) >= 0);
245 pt
= pa_proplist_to_string_sep(s
->proplist
, "\n ");
246 pa_log_info("Created sink %u \"%s\" with sample spec %s and channel map %s\n %s",
249 pa_sample_spec_snprint(st
, sizeof(st
), &s
->sample_spec
),
250 pa_channel_map_snprint(cm
, sizeof(cm
), &s
->channel_map
),
254 pa_source_new_data_init(&source_data
);
255 pa_source_new_data_set_sample_spec(&source_data
, &s
->sample_spec
);
256 pa_source_new_data_set_channel_map(&source_data
, &s
->channel_map
);
257 source_data
.name
= pa_sprintf_malloc("%s.monitor", name
);
258 source_data
.driver
= data
->driver
;
259 source_data
.module
= data
->module
;
260 source_data
.card
= data
->card
;
262 dn
= pa_proplist_gets(s
->proplist
, PA_PROP_DEVICE_DESCRIPTION
);
263 pa_proplist_setf(source_data
.proplist
, PA_PROP_DEVICE_DESCRIPTION
, "Monitor of %s", dn
? dn
: s
->name
);
264 pa_proplist_sets(source_data
.proplist
, PA_PROP_DEVICE_CLASS
, "monitor");
266 s
->monitor_source
= pa_source_new(core
, &source_data
, 0);
268 pa_source_new_data_done(&source_data
);
270 if (!s
->monitor_source
) {
276 s
->monitor_source
->monitor_of
= s
;
278 pa_source_set_latency_range(s
->monitor_source
, s
->thread_info
.min_latency
, s
->thread_info
.max_latency
);
279 pa_source_set_max_rewind(s
->monitor_source
, s
->thread_info
.max_rewind
);
284 /* Called from main context */
285 static int sink_set_state(pa_sink
*s
, pa_sink_state_t state
) {
287 pa_bool_t suspend_change
;
288 pa_sink_state_t original_state
;
292 if (s
->state
== state
)
295 original_state
= s
->state
;
298 (original_state
== PA_SINK_SUSPENDED
&& PA_SINK_IS_OPENED(state
)) ||
299 (PA_SINK_IS_OPENED(original_state
) && state
== PA_SINK_SUSPENDED
);
302 if ((ret
= s
->set_state(s
, state
)) < 0)
306 if ((ret
= pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_SET_STATE
, PA_UINT_TO_PTR(state
), 0, NULL
)) < 0) {
309 s
->set_state(s
, original_state
);
316 if (state
!= PA_SINK_UNLINKED
) { /* if we enter UNLINKED state pa_sink_unlink() will fire the apropriate events */
317 pa_hook_fire(&s
->core
->hooks
[PA_CORE_HOOK_SINK_STATE_CHANGED
], s
);
318 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
| PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
321 if (suspend_change
) {
325 /* We're suspending or resuming, tell everyone about it */
327 for (i
= PA_SINK_INPUT(pa_idxset_first(s
->inputs
, &idx
)); i
; i
= PA_SINK_INPUT(pa_idxset_next(s
->inputs
, &idx
)))
328 if (s
->state
== PA_SINK_SUSPENDED
&&
329 (i
->flags
& PA_SINK_INPUT_FAIL_ON_SUSPEND
))
330 pa_sink_input_kill(i
);
332 i
->suspend(i
, state
== PA_SINK_SUSPENDED
);
334 if (s
->monitor_source
)
335 pa_source_sync_suspend(s
->monitor_source
);
341 /* Called from main context */
342 void pa_sink_put(pa_sink
* s
) {
343 pa_sink_assert_ref(s
);
345 pa_assert(s
->state
== PA_SINK_INIT
);
347 /* The following fields must be initialized properly when calling _put() */
348 pa_assert(s
->asyncmsgq
);
349 pa_assert(s
->rtpoll
);
350 pa_assert(s
->thread_info
.min_latency
<= s
->thread_info
.max_latency
);
352 if (!(s
->flags
& PA_SINK_HW_VOLUME_CTRL
)) {
353 s
->flags
|= PA_SINK_DECIBEL_VOLUME
;
355 s
->thread_info
.soft_volume
= s
->soft_volume
;
356 s
->thread_info
.soft_muted
= s
->muted
;
359 if (s
->flags
& PA_SINK_DECIBEL_VOLUME
)
360 s
->n_volume_steps
= PA_VOLUME_NORM
+1;
362 if (s
->core
->flat_volumes
)
363 if (s
->flags
& PA_SINK_DECIBEL_VOLUME
)
364 s
->flags
|= PA_SINK_FLAT_VOLUME
;
366 if (s
->flags
& PA_SINK_LATENCY
)
367 s
->monitor_source
->flags
|= PA_SOURCE_LATENCY
;
369 if (s
->flags
& PA_SINK_DYNAMIC_LATENCY
) {
370 s
->monitor_source
->flags
|= PA_SOURCE_DYNAMIC_LATENCY
;
371 s
->fixed_latency
= 0;
372 } else if (s
->fixed_latency
<= 0)
373 s
->fixed_latency
= DEFAULT_FIXED_LATENCY
;
375 s
->monitor_source
->fixed_latency
= s
->fixed_latency
;
377 pa_assert_se(sink_set_state(s
, PA_SINK_IDLE
) == 0);
379 pa_source_put(s
->monitor_source
);
381 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
| PA_SUBSCRIPTION_EVENT_NEW
, s
->index
);
382 pa_hook_fire(&s
->core
->hooks
[PA_CORE_HOOK_SINK_PUT
], s
);
385 /* Called from main context */
386 void pa_sink_unlink(pa_sink
* s
) {
388 pa_sink_input
*i
, *j
= NULL
;
392 /* Please note that pa_sink_unlink() does more than simply
393 * reversing pa_sink_put(). It also undoes the registrations
394 * already done in pa_sink_new()! */
396 /* All operations here shall be idempotent, i.e. pa_sink_unlink()
397 * may be called multiple times on the same sink without bad
400 linked
= PA_SINK_IS_LINKED(s
->state
);
403 pa_hook_fire(&s
->core
->hooks
[PA_CORE_HOOK_SINK_UNLINK
], s
);
405 if (s
->state
!= PA_SINK_UNLINKED
)
406 pa_namereg_unregister(s
->core
, s
->name
);
407 pa_idxset_remove_by_data(s
->core
->sinks
, s
, NULL
);
410 pa_idxset_remove_by_data(s
->card
->sinks
, s
, NULL
);
412 while ((i
= pa_idxset_first(s
->inputs
, NULL
))) {
414 pa_sink_input_kill(i
);
419 sink_set_state(s
, PA_SINK_UNLINKED
);
421 s
->state
= PA_SINK_UNLINKED
;
425 if (s
->monitor_source
)
426 pa_source_unlink(s
->monitor_source
);
429 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
| PA_SUBSCRIPTION_EVENT_REMOVE
, s
->index
);
430 pa_hook_fire(&s
->core
->hooks
[PA_CORE_HOOK_SINK_UNLINK_POST
], s
);
434 /* Called from main context */
435 static void sink_free(pa_object
*o
) {
436 pa_sink
*s
= PA_SINK(o
);
440 pa_assert(pa_sink_refcnt(s
) == 0);
442 if (PA_SINK_IS_LINKED(s
->state
))
445 pa_log_info("Freeing sink %u \"%s\"", s
->index
, s
->name
);
447 if (s
->monitor_source
) {
448 pa_source_unref(s
->monitor_source
);
449 s
->monitor_source
= NULL
;
452 pa_idxset_free(s
->inputs
, NULL
, NULL
);
454 while ((i
= pa_hashmap_steal_first(s
->thread_info
.inputs
)))
455 pa_sink_input_unref(i
);
457 pa_hashmap_free(s
->thread_info
.inputs
, NULL
, NULL
);
459 if (s
->silence
.memblock
)
460 pa_memblock_unref(s
->silence
.memblock
);
466 pa_proplist_free(s
->proplist
);
471 /* Called from main context */
472 void pa_sink_set_asyncmsgq(pa_sink
*s
, pa_asyncmsgq
*q
) {
473 pa_sink_assert_ref(s
);
477 if (s
->monitor_source
)
478 pa_source_set_asyncmsgq(s
->monitor_source
, q
);
481 /* Called from main context */
482 void pa_sink_set_rtpoll(pa_sink
*s
, pa_rtpoll
*p
) {
483 pa_sink_assert_ref(s
);
486 if (s
->monitor_source
)
487 pa_source_set_rtpoll(s
->monitor_source
, p
);
490 /* Called from main context */
491 int pa_sink_update_status(pa_sink
*s
) {
492 pa_sink_assert_ref(s
);
493 pa_assert(PA_SINK_IS_LINKED(s
->state
));
495 if (s
->state
== PA_SINK_SUSPENDED
)
498 return sink_set_state(s
, pa_sink_used_by(s
) ? PA_SINK_RUNNING
: PA_SINK_IDLE
);
501 /* Called from main context */
502 int pa_sink_suspend(pa_sink
*s
, pa_bool_t suspend
) {
503 pa_sink_assert_ref(s
);
504 pa_assert(PA_SINK_IS_LINKED(s
->state
));
507 return sink_set_state(s
, PA_SINK_SUSPENDED
);
509 return sink_set_state(s
, pa_sink_used_by(s
) ? PA_SINK_RUNNING
: PA_SINK_IDLE
);
512 /* Called from main context */
513 pa_queue
*pa_sink_move_all_start(pa_sink
*s
) {
515 pa_sink_input
*i
, *n
;
518 pa_sink_assert_ref(s
);
519 pa_assert(PA_SINK_IS_LINKED(s
->state
));
523 for (i
= PA_SINK_INPUT(pa_idxset_first(s
->inputs
, &idx
)); i
; i
= n
) {
524 n
= PA_SINK_INPUT(pa_idxset_next(s
->inputs
, &idx
));
526 pa_sink_input_ref(i
);
528 if (pa_sink_input_start_move(i
) >= 0)
531 pa_sink_input_unref(i
);
537 /* Called from main context */
538 void pa_sink_move_all_finish(pa_sink
*s
, pa_queue
*q
, pa_bool_t save
) {
541 pa_sink_assert_ref(s
);
542 pa_assert(PA_SINK_IS_LINKED(s
->state
));
545 while ((i
= PA_SINK_INPUT(pa_queue_pop(q
)))) {
546 if (pa_sink_input_finish_move(i
, s
, save
) < 0)
547 pa_sink_input_kill(i
);
549 pa_sink_input_unref(i
);
552 pa_queue_free(q
, NULL
, NULL
);
555 /* Called from main context */
556 void pa_sink_move_all_fail(pa_queue
*q
) {
560 while ((i
= PA_SINK_INPUT(pa_queue_pop(q
)))) {
561 if (pa_hook_fire(&i
->core
->hooks
[PA_CORE_HOOK_SINK_INPUT_MOVE_FAIL
], i
) == PA_HOOK_OK
) {
562 pa_sink_input_kill(i
);
563 pa_sink_input_unref(i
);
567 pa_queue_free(q
, NULL
, NULL
);
570 /* Called from IO thread context */
571 void pa_sink_process_rewind(pa_sink
*s
, size_t nbytes
) {
574 pa_sink_assert_ref(s
);
575 pa_assert(PA_SINK_IS_LINKED(s
->thread_info
.state
));
577 /* If nobody requested this and this is actually no real rewind
578 * then we can short cut this */
579 if (!s
->thread_info
.rewind_requested
&& nbytes
<= 0)
582 s
->thread_info
.rewind_nbytes
= 0;
583 s
->thread_info
.rewind_requested
= FALSE
;
585 if (s
->thread_info
.state
== PA_SINK_SUSPENDED
)
589 pa_log_debug("Processing rewind...");
591 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
))) {
592 pa_sink_input_assert_ref(i
);
593 pa_sink_input_process_rewind(i
, nbytes
);
597 if (s
->monitor_source
&& PA_SOURCE_IS_LINKED(s
->monitor_source
->thread_info
.state
))
598 pa_source_process_rewind(s
->monitor_source
, nbytes
);
601 /* Called from IO thread context */
602 static unsigned fill_mix_info(pa_sink
*s
, size_t *length
, pa_mix_info
*info
, unsigned maxinfo
) {
606 size_t mixlength
= *length
;
608 pa_sink_assert_ref(s
);
611 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
)) && maxinfo
> 0) {
612 pa_sink_input_assert_ref(i
);
614 pa_sink_input_peek(i
, *length
, &info
->chunk
, &info
->volume
);
616 if (mixlength
== 0 || info
->chunk
.length
< mixlength
)
617 mixlength
= info
->chunk
.length
;
619 if (pa_memblock_is_silence(info
->chunk
.memblock
)) {
620 pa_memblock_unref(info
->chunk
.memblock
);
624 info
->userdata
= pa_sink_input_ref(i
);
626 pa_assert(info
->chunk
.memblock
);
627 pa_assert(info
->chunk
.length
> 0);
640 /* Called from IO thread context */
641 static void inputs_drop(pa_sink
*s
, pa_mix_info
*info
, unsigned n
, pa_memchunk
*result
) {
645 unsigned n_unreffed
= 0;
647 pa_sink_assert_ref(s
);
649 pa_assert(result
->memblock
);
650 pa_assert(result
->length
> 0);
652 /* We optimize for the case where the order of the inputs has not changed */
654 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
))) {
656 pa_mix_info
* m
= NULL
;
658 pa_sink_input_assert_ref(i
);
660 /* Let's try to find the matching entry info the pa_mix_info array */
661 for (j
= 0; j
< n
; j
++) {
663 if (info
[p
].userdata
== i
) {
674 pa_sink_input_drop(i
, result
->length
);
676 if (s
->monitor_source
&& PA_SOURCE_IS_LINKED(s
->monitor_source
->thread_info
.state
)) {
678 if (pa_hashmap_size(i
->thread_info
.direct_outputs
) > 0) {
683 if (m
&& m
->chunk
.memblock
) {
685 pa_memblock_ref(c
.memblock
);
686 pa_assert(result
->length
<= c
.length
);
687 c
.length
= result
->length
;
689 pa_memchunk_make_writable(&c
, 0);
690 pa_volume_memchunk(&c
, &s
->sample_spec
, &m
->volume
);
693 pa_memblock_ref(c
.memblock
);
694 pa_assert(result
->length
<= c
.length
);
695 c
.length
= result
->length
;
698 while ((o
= pa_hashmap_iterate(i
->thread_info
.direct_outputs
, &ostate
, NULL
))) {
699 pa_source_output_assert_ref(o
);
700 pa_assert(o
->direct_on_input
== i
);
701 pa_source_post_direct(s
->monitor_source
, o
, &c
);
704 pa_memblock_unref(c
.memblock
);
709 if (m
->chunk
.memblock
)
710 pa_memblock_unref(m
->chunk
.memblock
);
711 pa_memchunk_reset(&m
->chunk
);
713 pa_sink_input_unref(m
->userdata
);
720 /* Now drop references to entries that are included in the
721 * pa_mix_info array but don't exist anymore */
723 if (n_unreffed
< n
) {
724 for (; n
> 0; info
++, n
--) {
726 pa_sink_input_unref(info
->userdata
);
727 if (info
->chunk
.memblock
)
728 pa_memblock_unref(info
->chunk
.memblock
);
732 if (s
->monitor_source
&& PA_SOURCE_IS_LINKED(s
->monitor_source
->thread_info
.state
))
733 pa_source_post(s
->monitor_source
, result
);
736 /* Called from IO thread context */
737 void pa_sink_render(pa_sink
*s
, size_t length
, pa_memchunk
*result
) {
738 pa_mix_info info
[MAX_MIX_CHANNELS
];
740 size_t block_size_max
;
742 pa_sink_assert_ref(s
);
743 pa_assert(PA_SINK_IS_LINKED(s
->thread_info
.state
));
744 pa_assert(pa_frame_aligned(length
, &s
->sample_spec
));
749 pa_assert(!s
->thread_info
.rewind_requested
);
750 pa_assert(s
->thread_info
.rewind_nbytes
== 0);
752 if (s
->thread_info
.state
== PA_SINK_SUSPENDED
) {
753 result
->memblock
= pa_memblock_ref(s
->silence
.memblock
);
754 result
->index
= s
->silence
.index
;
755 result
->length
= PA_MIN(s
->silence
.length
, length
);
760 length
= pa_frame_align(MIX_BUFFER_LENGTH
, &s
->sample_spec
);
762 block_size_max
= pa_mempool_block_size_max(s
->core
->mempool
);
763 if (length
> block_size_max
)
764 length
= pa_frame_align(block_size_max
, &s
->sample_spec
);
766 pa_assert(length
> 0);
768 n
= fill_mix_info(s
, &length
, info
, MAX_MIX_CHANNELS
);
772 *result
= s
->silence
;
773 pa_memblock_ref(result
->memblock
);
775 if (result
->length
> length
)
776 result
->length
= length
;
781 *result
= info
[0].chunk
;
782 pa_memblock_ref(result
->memblock
);
784 if (result
->length
> length
)
785 result
->length
= length
;
787 pa_sw_cvolume_multiply(&volume
, &s
->thread_info
.soft_volume
, &info
[0].volume
);
789 if (s
->thread_info
.soft_muted
|| !pa_cvolume_is_norm(&volume
)) {
790 pa_memchunk_make_writable(result
, 0);
791 if (s
->thread_info
.soft_muted
|| pa_cvolume_is_muted(&volume
))
792 pa_silence_memchunk(result
, &s
->sample_spec
);
794 pa_volume_memchunk(result
, &s
->sample_spec
, &volume
);
798 result
->memblock
= pa_memblock_new(s
->core
->mempool
, length
);
800 ptr
= pa_memblock_acquire(result
->memblock
);
801 result
->length
= pa_mix(info
, n
,
804 &s
->thread_info
.soft_volume
,
805 s
->thread_info
.soft_muted
);
806 pa_memblock_release(result
->memblock
);
811 inputs_drop(s
, info
, n
, result
);
816 /* Called from IO thread context */
817 void pa_sink_render_into(pa_sink
*s
, pa_memchunk
*target
) {
818 pa_mix_info info
[MAX_MIX_CHANNELS
];
820 size_t length
, block_size_max
;
822 pa_sink_assert_ref(s
);
823 pa_assert(PA_SINK_IS_LINKED(s
->thread_info
.state
));
825 pa_assert(target
->memblock
);
826 pa_assert(target
->length
> 0);
827 pa_assert(pa_frame_aligned(target
->length
, &s
->sample_spec
));
831 pa_assert(!s
->thread_info
.rewind_requested
);
832 pa_assert(s
->thread_info
.rewind_nbytes
== 0);
834 if (s
->thread_info
.state
== PA_SINK_SUSPENDED
) {
835 pa_silence_memchunk(target
, &s
->sample_spec
);
839 length
= target
->length
;
840 block_size_max
= pa_mempool_block_size_max(s
->core
->mempool
);
841 if (length
> block_size_max
)
842 length
= pa_frame_align(block_size_max
, &s
->sample_spec
);
844 pa_assert(length
> 0);
846 n
= fill_mix_info(s
, &length
, info
, MAX_MIX_CHANNELS
);
849 if (target
->length
> length
)
850 target
->length
= length
;
852 pa_silence_memchunk(target
, &s
->sample_spec
);
856 if (target
->length
> length
)
857 target
->length
= length
;
859 pa_sw_cvolume_multiply(&volume
, &s
->thread_info
.soft_volume
, &info
[0].volume
);
861 if (s
->thread_info
.soft_muted
|| pa_cvolume_is_muted(&volume
))
862 pa_silence_memchunk(target
, &s
->sample_spec
);
866 vchunk
= info
[0].chunk
;
867 pa_memblock_ref(vchunk
.memblock
);
869 if (vchunk
.length
> length
)
870 vchunk
.length
= length
;
872 if (!pa_cvolume_is_norm(&volume
)) {
873 pa_memchunk_make_writable(&vchunk
, 0);
874 pa_volume_memchunk(&vchunk
, &s
->sample_spec
, &volume
);
877 pa_memchunk_memcpy(target
, &vchunk
);
878 pa_memblock_unref(vchunk
.memblock
);
884 ptr
= pa_memblock_acquire(target
->memblock
);
886 target
->length
= pa_mix(info
, n
,
887 (uint8_t*) ptr
+ target
->index
, length
,
889 &s
->thread_info
.soft_volume
,
890 s
->thread_info
.soft_muted
);
892 pa_memblock_release(target
->memblock
);
895 inputs_drop(s
, info
, n
, target
);
900 /* Called from IO thread context */
901 void pa_sink_render_into_full(pa_sink
*s
, pa_memchunk
*target
) {
905 pa_sink_assert_ref(s
);
906 pa_assert(PA_SINK_IS_LINKED(s
->thread_info
.state
));
908 pa_assert(target
->memblock
);
909 pa_assert(target
->length
> 0);
910 pa_assert(pa_frame_aligned(target
->length
, &s
->sample_spec
));
914 pa_assert(!s
->thread_info
.rewind_requested
);
915 pa_assert(s
->thread_info
.rewind_nbytes
== 0);
924 pa_sink_render_into(s
, &chunk
);
933 /* Called from IO thread context */
934 void pa_sink_render_full(pa_sink
*s
, size_t length
, pa_memchunk
*result
) {
935 pa_sink_assert_ref(s
);
936 pa_assert(PA_SINK_IS_LINKED(s
->thread_info
.state
));
937 pa_assert(length
> 0);
938 pa_assert(pa_frame_aligned(length
, &s
->sample_spec
));
941 pa_assert(!s
->thread_info
.rewind_requested
);
942 pa_assert(s
->thread_info
.rewind_nbytes
== 0);
944 /*** This needs optimization ***/
947 result
->length
= length
;
948 result
->memblock
= pa_memblock_new(s
->core
->mempool
, length
);
950 pa_sink_render_into_full(s
, result
);
953 /* Called from main thread */
954 pa_usec_t
pa_sink_get_latency(pa_sink
*s
) {
957 pa_sink_assert_ref(s
);
958 pa_assert(PA_SINK_IS_LINKED(s
->state
));
960 /* The returned value is supposed to be in the time domain of the sound card! */
962 if (s
->state
== PA_SINK_SUSPENDED
)
965 if (!(s
->flags
& PA_SINK_LATENCY
))
968 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_GET_LATENCY
, &usec
, 0, NULL
) == 0);
973 /* Called from IO thread */
974 pa_usec_t
pa_sink_get_latency_within_thread(pa_sink
*s
) {
978 pa_sink_assert_ref(s
);
979 pa_assert(PA_SINK_IS_LINKED(s
->thread_info
.state
));
981 /* The returned value is supposed to be in the time domain of the sound card! */
983 if (s
->thread_info
.state
== PA_SINK_SUSPENDED
)
986 if (!(s
->flags
& PA_SINK_LATENCY
))
991 /* We probably should make this a proper vtable callback instead of going through process_msg() */
993 if (o
->process_msg(o
, PA_SINK_MESSAGE_GET_LATENCY
, &usec
, 0, NULL
) < 0)
999 static void compute_new_soft_volume(pa_sink_input
*i
, const pa_cvolume
*new_volume
) {
1002 pa_sink_input_assert_ref(i
);
1003 pa_assert(new_volume
->channels
== i
->sample_spec
.channels
);
1006 * This basically calculates:
1008 * i->relative_volume := i->virtual_volume / new_volume
1009 * i->soft_volume := i->relative_volume * i->volume_factor
1012 /* The new sink volume passed in here must already be remapped to
1013 * the sink input's channel map! */
1015 i
->soft_volume
.channels
= i
->sample_spec
.channels
;
1017 for (c
= 0; c
< i
->sample_spec
.channels
; c
++)
1019 if (new_volume
->values
[c
] <= PA_VOLUME_MUTED
)
1020 /* We leave i->relative_volume untouched */
1021 i
->soft_volume
.values
[c
] = PA_VOLUME_MUTED
;
1023 i
->relative_volume
[c
] =
1024 pa_sw_volume_to_linear(i
->virtual_volume
.values
[c
]) /
1025 pa_sw_volume_to_linear(new_volume
->values
[c
]);
1027 i
->soft_volume
.values
[c
] = pa_sw_volume_from_linear(
1028 i
->relative_volume
[c
] *
1029 pa_sw_volume_to_linear(i
->volume_factor
.values
[c
]));
1032 /* Hooks have the ability to play games with i->soft_volume */
1033 pa_hook_fire(&i
->core
->hooks
[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME
], i
);
1035 /* We don't copy the soft_volume to the thread_info data
1036 * here. That must be done by the caller */
1039 /* Called from main thread */
1040 void pa_sink_update_flat_volume(pa_sink
*s
, pa_cvolume
*new_volume
) {
1044 pa_sink_assert_ref(s
);
1045 pa_assert(new_volume
);
1046 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1047 pa_assert(s
->flags
& PA_SINK_FLAT_VOLUME
);
1049 /* This is called whenever a sink input volume changes and we
1050 * might need to fix up the sink volume accordingly. Please note
1051 * that we don't actually update the sinks volume here, we only
1052 * return how it needs to be updated. The caller should then call
1053 * pa_sink_set_volume().*/
1055 if (pa_idxset_isempty(s
->inputs
)) {
1056 /* In the special case that we have no sink input we leave the
1057 * volume unmodified. */
1058 *new_volume
= s
->virtual_volume
;
1062 pa_cvolume_mute(new_volume
, s
->channel_map
.channels
);
1064 /* First let's determine the new maximum volume of all inputs
1065 * connected to this sink */
1066 for (i
= PA_SINK_INPUT(pa_idxset_first(s
->inputs
, &idx
)); i
; i
= PA_SINK_INPUT(pa_idxset_next(s
->inputs
, &idx
))) {
1068 pa_cvolume remapped_volume
;
1070 remapped_volume
= i
->virtual_volume
;
1071 pa_cvolume_remap(&remapped_volume
, &i
->channel_map
, &s
->channel_map
);
1073 for (c
= 0; c
< new_volume
->channels
; c
++)
1074 if (remapped_volume
.values
[c
] > new_volume
->values
[c
])
1075 new_volume
->values
[c
] = remapped_volume
.values
[c
];
1078 /* Then, let's update the soft volumes of all inputs connected
1080 for (i
= PA_SINK_INPUT(pa_idxset_first(s
->inputs
, &idx
)); i
; i
= PA_SINK_INPUT(pa_idxset_next(s
->inputs
, &idx
))) {
1081 pa_cvolume remapped_new_volume
;
1083 remapped_new_volume
= *new_volume
;
1084 pa_cvolume_remap(&remapped_new_volume
, &s
->channel_map
, &i
->channel_map
);
1085 compute_new_soft_volume(i
, &remapped_new_volume
);
1087 /* We don't copy soft_volume to the thread_info data here
1088 * (i.e. issue PA_SINK_INPUT_MESSAGE_SET_VOLUME) because we
1089 * want the update to be atomically with the sink volume
1090 * update, hence we do it within the pa_sink_set_volume() call
1095 /* Called from main thread */
1096 void pa_sink_propagate_flat_volume(pa_sink
*s
) {
1100 pa_sink_assert_ref(s
);
1101 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1102 pa_assert(s
->flags
& PA_SINK_FLAT_VOLUME
);
1104 /* This is called whenever the sink volume changes that is not
1105 * caused by a sink input volume change. We need to fix up the
1106 * sink input volumes accordingly */
1108 for (i
= PA_SINK_INPUT(pa_idxset_first(s
->inputs
, &idx
)); i
; i
= PA_SINK_INPUT(pa_idxset_next(s
->inputs
, &idx
))) {
1109 pa_cvolume sink_volume
, new_virtual_volume
;
1112 /* This basically calculates i->virtual_volume := i->relative_volume * s->virtual_volume */
1114 sink_volume
= s
->virtual_volume
;
1115 pa_cvolume_remap(&sink_volume
, &s
->channel_map
, &i
->channel_map
);
1117 for (c
= 0; c
< i
->sample_spec
.channels
; c
++)
1118 new_virtual_volume
.values
[c
] = pa_sw_volume_from_linear(
1119 i
->relative_volume
[c
] *
1120 pa_sw_volume_to_linear(sink_volume
.values
[c
]));
1122 new_virtual_volume
.channels
= i
->sample_spec
.channels
;
1124 if (!pa_cvolume_equal(&new_virtual_volume
, &i
->virtual_volume
)) {
1125 i
->virtual_volume
= new_virtual_volume
;
1127 /* Hmm, the soft volume might no longer actually match
1128 * what has been chosen as new virtual volume here,
1129 * especially when the old volume was
1130 * PA_VOLUME_MUTED. Hence let's recalculate the soft
1132 compute_new_soft_volume(i
, &sink_volume
);
1134 /* The virtual volume changed, let's tell people so */
1135 pa_subscription_post(i
->core
, PA_SUBSCRIPTION_EVENT_SINK_INPUT
|PA_SUBSCRIPTION_EVENT_CHANGE
, i
->index
);
1139 /* If the soft_volume of any of the sink inputs got changed, let's
1140 * make sure the thread copies are synced up. */
1141 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_SYNC_VOLUMES
, NULL
, 0, NULL
) == 0);
1144 /* Called from main thread */
1145 void pa_sink_set_volume(pa_sink
*s
, const pa_cvolume
*volume
, pa_bool_t propagate
, pa_bool_t sendmsg
) {
1146 pa_bool_t virtual_volume_changed
;
1148 pa_sink_assert_ref(s
);
1149 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1151 pa_assert(pa_cvolume_valid(volume
));
1152 pa_assert(pa_cvolume_compatible(volume
, &s
->sample_spec
));
1154 virtual_volume_changed
= !pa_cvolume_equal(volume
, &s
->virtual_volume
);
1155 s
->virtual_volume
= *volume
;
1157 /* Propagate this volume change back to the inputs */
1158 if (virtual_volume_changed
)
1159 if (propagate
&& (s
->flags
& PA_SINK_FLAT_VOLUME
))
1160 pa_sink_propagate_flat_volume(s
);
1162 if (s
->set_volume
) {
1163 /* If we have a function set_volume(), then we do not apply a
1164 * soft volume by default. However, set_volume() is apply one
1165 * to s->soft_volume */
1167 pa_cvolume_reset(&s
->soft_volume
, s
->sample_spec
.channels
);
1171 /* If we have no function set_volume(), then the soft volume
1172 * becomes the virtual volume */
1173 s
->soft_volume
= s
->virtual_volume
;
1175 /* This tells the sink that soft and/or virtual volume changed */
1177 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_SET_VOLUME
, NULL
, 0, NULL
) == 0);
1179 if (virtual_volume_changed
)
1180 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
1183 /* Called from main thread. Only to be called by sink implementor */
1184 void pa_sink_set_soft_volume(pa_sink
*s
, const pa_cvolume
*volume
) {
1185 pa_sink_assert_ref(s
);
1188 s
->soft_volume
= *volume
;
1190 if (PA_SINK_IS_LINKED(s
->state
))
1191 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_SET_VOLUME
, NULL
, 0, NULL
) == 0);
1193 s
->thread_info
.soft_volume
= *volume
;
1196 /* Called from main thread */
1197 const pa_cvolume
*pa_sink_get_volume(pa_sink
*s
, pa_bool_t force_refresh
) {
1198 pa_sink_assert_ref(s
);
1200 if (s
->refresh_volume
|| force_refresh
) {
1201 struct pa_cvolume old_virtual_volume
= s
->virtual_volume
;
1206 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_GET_VOLUME
, NULL
, 0, NULL
) == 0);
1208 if (!pa_cvolume_equal(&old_virtual_volume
, &s
->virtual_volume
)) {
1210 if (s
->flags
& PA_SINK_FLAT_VOLUME
)
1211 pa_sink_propagate_flat_volume(s
);
1213 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
1217 return &s
->virtual_volume
;
1220 /* Called from main thread */
1221 void pa_sink_volume_changed(pa_sink
*s
, const pa_cvolume
*new_volume
) {
1222 pa_sink_assert_ref(s
);
1224 /* The sink implementor may call this if the volume changed to make sure everyone is notified */
1226 if (pa_cvolume_equal(&s
->virtual_volume
, new_volume
))
1229 s
->virtual_volume
= *new_volume
;
1230 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
1233 /* Called from main thread */
1234 void pa_sink_set_mute(pa_sink
*s
, pa_bool_t mute
) {
1235 pa_bool_t old_muted
;
1237 pa_sink_assert_ref(s
);
1238 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1240 old_muted
= s
->muted
;
1246 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_SET_MUTE
, NULL
, 0, NULL
) == 0);
1248 if (old_muted
!= s
->muted
)
1249 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
1252 /* Called from main thread */
1253 pa_bool_t
pa_sink_get_mute(pa_sink
*s
, pa_bool_t force_refresh
) {
1255 pa_sink_assert_ref(s
);
1257 if (s
->refresh_muted
|| force_refresh
) {
1258 pa_bool_t old_muted
= s
->muted
;
1263 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_GET_MUTE
, NULL
, 0, NULL
) == 0);
1265 if (old_muted
!= s
->muted
)
1266 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
1272 /* Called from main thread */
1273 void pa_sink_mute_changed(pa_sink
*s
, pa_bool_t new_muted
) {
1274 pa_sink_assert_ref(s
);
1276 /* The sink implementor may call this if the volume changed to make sure everyone is notified */
1278 if (s
->muted
== new_muted
)
1281 s
->muted
= new_muted
;
1282 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
1285 /* Called from main thread */
1286 pa_bool_t
pa_sink_update_proplist(pa_sink
*s
, pa_update_mode_t mode
, pa_proplist
*p
) {
1287 pa_sink_assert_ref(s
);
1290 pa_proplist_update(s
->proplist
, mode
, p
);
1292 if (PA_SINK_IS_LINKED(s
->state
)) {
1293 pa_hook_fire(&s
->core
->hooks
[PA_CORE_HOOK_SINK_PROPLIST_CHANGED
], s
);
1294 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
1300 /* Called from main thread */
1301 void pa_sink_set_description(pa_sink
*s
, const char *description
) {
1303 pa_sink_assert_ref(s
);
1305 if (!description
&& !pa_proplist_contains(s
->proplist
, PA_PROP_DEVICE_DESCRIPTION
))
1308 old
= pa_proplist_gets(s
->proplist
, PA_PROP_DEVICE_DESCRIPTION
);
1310 if (old
&& description
&& !strcmp(old
, description
))
1314 pa_proplist_sets(s
->proplist
, PA_PROP_DEVICE_DESCRIPTION
, description
);
1316 pa_proplist_unset(s
->proplist
, PA_PROP_DEVICE_DESCRIPTION
);
1318 if (s
->monitor_source
) {
1321 n
= pa_sprintf_malloc("Monitor Source of %s", description
? description
: s
->name
);
1322 pa_source_set_description(s
->monitor_source
, n
);
1326 if (PA_SINK_IS_LINKED(s
->state
)) {
1327 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
1328 pa_hook_fire(&s
->core
->hooks
[PA_CORE_HOOK_SINK_PROPLIST_CHANGED
], s
);
1332 /* Called from main thread */
1333 unsigned pa_sink_linked_by(pa_sink
*s
) {
1336 pa_sink_assert_ref(s
);
1337 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1339 ret
= pa_idxset_size(s
->inputs
);
1341 /* We add in the number of streams connected to us here. Please
1342 * note the asymmmetry to pa_sink_used_by()! */
1344 if (s
->monitor_source
)
1345 ret
+= pa_source_linked_by(s
->monitor_source
);
1350 /* Called from main thread */
1351 unsigned pa_sink_used_by(pa_sink
*s
) {
1354 pa_sink_assert_ref(s
);
1355 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1357 ret
= pa_idxset_size(s
->inputs
);
1358 pa_assert(ret
>= s
->n_corked
);
1360 /* Streams connected to our monitor source do not matter for
1361 * pa_sink_used_by()!.*/
1363 return ret
- s
->n_corked
;
1366 /* Called from main thread */
1367 unsigned pa_sink_check_suspend(pa_sink
*s
) {
1372 pa_sink_assert_ref(s
);
1374 if (!PA_SINK_IS_LINKED(s
->state
))
1379 for (i
= PA_SINK_INPUT(pa_idxset_first(s
->inputs
, &idx
)); i
; i
= PA_SINK_INPUT(pa_idxset_next(s
->inputs
, &idx
))) {
1380 pa_sink_input_state_t st
;
1382 st
= pa_sink_input_get_state(i
);
1383 pa_assert(PA_SINK_INPUT_IS_LINKED(st
));
1385 if (st
== PA_SINK_INPUT_CORKED
)
1388 if (i
->flags
& PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND
)
1394 if (s
->monitor_source
)
1395 ret
+= pa_source_check_suspend(s
->monitor_source
);
1400 /* Called from the IO thread */
1401 static void sync_input_volumes_within_thread(pa_sink
*s
) {
1405 pa_sink_assert_ref(s
);
1407 while ((i
= PA_SINK_INPUT(pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
)))) {
1408 if (pa_cvolume_equal(&i
->thread_info
.soft_volume
, &i
->soft_volume
))
1411 i
->thread_info
.soft_volume
= i
->soft_volume
;
1412 pa_sink_input_request_rewind(i
, 0, TRUE
, FALSE
, FALSE
);
1416 /* Called from IO thread, except when it is not */
1417 int pa_sink_process_msg(pa_msgobject
*o
, int code
, void *userdata
, int64_t offset
, pa_memchunk
*chunk
) {
1418 pa_sink
*s
= PA_SINK(o
);
1419 pa_sink_assert_ref(s
);
1421 switch ((pa_sink_message_t
) code
) {
1423 case PA_SINK_MESSAGE_ADD_INPUT
: {
1424 pa_sink_input
*i
= PA_SINK_INPUT(userdata
);
1426 /* If you change anything here, make sure to change the
1427 * sink input handling a few lines down at
1428 * PA_SINK_MESSAGE_FINISH_MOVE, too. */
1430 pa_hashmap_put(s
->thread_info
.inputs
, PA_UINT32_TO_PTR(i
->index
), pa_sink_input_ref(i
));
1432 /* Since the caller sleeps in pa_sink_input_put(), we can
1433 * safely access data outside of thread_info even though
1436 if ((i
->thread_info
.sync_prev
= i
->sync_prev
)) {
1437 pa_assert(i
->sink
== i
->thread_info
.sync_prev
->sink
);
1438 pa_assert(i
->sync_prev
->sync_next
== i
);
1439 i
->thread_info
.sync_prev
->thread_info
.sync_next
= i
;
1442 if ((i
->thread_info
.sync_next
= i
->sync_next
)) {
1443 pa_assert(i
->sink
== i
->thread_info
.sync_next
->sink
);
1444 pa_assert(i
->sync_next
->sync_prev
== i
);
1445 i
->thread_info
.sync_next
->thread_info
.sync_prev
= i
;
1448 pa_assert(!i
->thread_info
.attached
);
1449 i
->thread_info
.attached
= TRUE
;
1454 pa_sink_input_set_state_within_thread(i
, i
->state
);
1456 /* The requested latency of the sink input needs to be
1457 * fixed up and then configured on the sink */
1459 if (i
->thread_info
.requested_sink_latency
!= (pa_usec_t
) -1)
1460 pa_sink_input_set_requested_latency_within_thread(i
, i
->thread_info
.requested_sink_latency
);
1462 pa_sink_input_update_max_rewind(i
, s
->thread_info
.max_rewind
);
1463 pa_sink_input_update_max_request(i
, s
->thread_info
.max_request
);
1465 /* We don't rewind here automatically. This is left to the
1466 * sink input implementor because some sink inputs need a
1467 * slow start, i.e. need some time to buffer client
1468 * samples before beginning streaming. */
1470 /* In flat volume mode we need to update the volume as
1472 return o
->process_msg(o
, PA_SINK_MESSAGE_SET_VOLUME
, NULL
, 0, NULL
);
1475 case PA_SINK_MESSAGE_REMOVE_INPUT
: {
1476 pa_sink_input
*i
= PA_SINK_INPUT(userdata
);
1478 /* If you change anything here, make sure to change the
1479 * sink input handling a few lines down at
1480 * PA_SINK_MESSAGE_PREPAPRE_MOVE, too. */
1485 pa_sink_input_set_state_within_thread(i
, i
->state
);
1487 pa_assert(i
->thread_info
.attached
);
1488 i
->thread_info
.attached
= FALSE
;
1490 /* Since the caller sleeps in pa_sink_input_unlink(),
1491 * we can safely access data outside of thread_info even
1492 * though it is mutable */
1494 pa_assert(!i
->sync_prev
);
1495 pa_assert(!i
->sync_next
);
1497 if (i
->thread_info
.sync_prev
) {
1498 i
->thread_info
.sync_prev
->thread_info
.sync_next
= i
->thread_info
.sync_prev
->sync_next
;
1499 i
->thread_info
.sync_prev
= NULL
;
1502 if (i
->thread_info
.sync_next
) {
1503 i
->thread_info
.sync_next
->thread_info
.sync_prev
= i
->thread_info
.sync_next
->sync_prev
;
1504 i
->thread_info
.sync_next
= NULL
;
1507 if (pa_hashmap_remove(s
->thread_info
.inputs
, PA_UINT32_TO_PTR(i
->index
)))
1508 pa_sink_input_unref(i
);
1510 pa_sink_invalidate_requested_latency(s
);
1511 pa_sink_request_rewind(s
, (size_t) -1);
1513 /* In flat volume mode we need to update the volume as
1515 return o
->process_msg(o
, PA_SINK_MESSAGE_SET_VOLUME
, NULL
, 0, NULL
);
1518 case PA_SINK_MESSAGE_START_MOVE
: {
1519 pa_sink_input
*i
= PA_SINK_INPUT(userdata
);
1521 /* We don't support moving synchronized streams. */
1522 pa_assert(!i
->sync_prev
);
1523 pa_assert(!i
->sync_next
);
1524 pa_assert(!i
->thread_info
.sync_next
);
1525 pa_assert(!i
->thread_info
.sync_prev
);
1527 if (i
->thread_info
.state
!= PA_SINK_INPUT_CORKED
) {
1529 size_t sink_nbytes
, total_nbytes
;
1531 /* Get the latency of the sink */
1532 if (!(s
->flags
& PA_SINK_LATENCY
) ||
1533 PA_MSGOBJECT(s
)->process_msg(PA_MSGOBJECT(s
), PA_SINK_MESSAGE_GET_LATENCY
, &usec
, 0, NULL
) < 0)
1536 sink_nbytes
= pa_usec_to_bytes(usec
, &s
->sample_spec
);
1537 total_nbytes
= sink_nbytes
+ pa_memblockq_get_length(i
->thread_info
.render_memblockq
);
1539 if (total_nbytes
> 0) {
1540 i
->thread_info
.rewrite_nbytes
= i
->thread_info
.resampler
? pa_resampler_request(i
->thread_info
.resampler
, total_nbytes
) : total_nbytes
;
1541 i
->thread_info
.rewrite_flush
= TRUE
;
1542 pa_sink_input_process_rewind(i
, sink_nbytes
);
1549 pa_assert(i
->thread_info
.attached
);
1550 i
->thread_info
.attached
= FALSE
;
1552 /* Let's remove the sink input ...*/
1553 if (pa_hashmap_remove(s
->thread_info
.inputs
, PA_UINT32_TO_PTR(i
->index
)))
1554 pa_sink_input_unref(i
);
1556 pa_sink_invalidate_requested_latency(s
);
1558 pa_log_debug("Requesting rewind due to started move");
1559 pa_sink_request_rewind(s
, (size_t) -1);
1561 /* In flat volume mode we need to update the volume as
1563 return o
->process_msg(o
, PA_SINK_MESSAGE_SET_VOLUME
, NULL
, 0, NULL
);
1566 case PA_SINK_MESSAGE_FINISH_MOVE
: {
1567 pa_sink_input
*i
= PA_SINK_INPUT(userdata
);
1569 /* We don't support moving synchronized streams. */
1570 pa_assert(!i
->sync_prev
);
1571 pa_assert(!i
->sync_next
);
1572 pa_assert(!i
->thread_info
.sync_next
);
1573 pa_assert(!i
->thread_info
.sync_prev
);
1575 pa_hashmap_put(s
->thread_info
.inputs
, PA_UINT32_TO_PTR(i
->index
), pa_sink_input_ref(i
));
1577 pa_assert(!i
->thread_info
.attached
);
1578 i
->thread_info
.attached
= TRUE
;
1583 if (i
->thread_info
.requested_sink_latency
!= (pa_usec_t
) -1)
1584 pa_sink_input_set_requested_latency_within_thread(i
, i
->thread_info
.requested_sink_latency
);
1586 pa_sink_input_update_max_rewind(i
, s
->thread_info
.max_rewind
);
1587 pa_sink_input_update_max_request(i
, s
->thread_info
.max_request
);
1589 if (i
->thread_info
.state
!= PA_SINK_INPUT_CORKED
) {
1593 /* Get the latency of the sink */
1594 if (!(s
->flags
& PA_SINK_LATENCY
) ||
1595 PA_MSGOBJECT(s
)->process_msg(PA_MSGOBJECT(s
), PA_SINK_MESSAGE_GET_LATENCY
, &usec
, 0, NULL
) < 0)
1598 nbytes
= pa_usec_to_bytes(usec
, &s
->sample_spec
);
1601 pa_sink_input_drop(i
, nbytes
);
1603 pa_log_debug("Requesting rewind due to finished move");
1604 pa_sink_request_rewind(s
, nbytes
);
1607 /* In flat volume mode we need to update the volume as
1609 return o
->process_msg(o
, PA_SINK_MESSAGE_SET_VOLUME
, NULL
, 0, NULL
);
1612 case PA_SINK_MESSAGE_SET_VOLUME
:
1614 if (!pa_cvolume_equal(&s
->thread_info
.soft_volume
, &s
->soft_volume
)) {
1615 s
->thread_info
.soft_volume
= s
->soft_volume
;
1616 pa_sink_request_rewind(s
, (size_t) -1);
1619 if (!(s
->flags
& PA_SINK_FLAT_VOLUME
))
1622 /* Fall through ... */
1624 case PA_SINK_MESSAGE_SYNC_VOLUMES
:
1625 sync_input_volumes_within_thread(s
);
1628 case PA_SINK_MESSAGE_GET_VOLUME
:
1631 case PA_SINK_MESSAGE_SET_MUTE
:
1633 if (s
->thread_info
.soft_muted
!= s
->muted
) {
1634 s
->thread_info
.soft_muted
= s
->muted
;
1635 pa_sink_request_rewind(s
, (size_t) -1);
1640 case PA_SINK_MESSAGE_GET_MUTE
:
1643 case PA_SINK_MESSAGE_SET_STATE
: {
1645 pa_bool_t suspend_change
=
1646 (s
->thread_info
.state
== PA_SINK_SUSPENDED
&& PA_SINK_IS_OPENED(PA_PTR_TO_UINT(userdata
))) ||
1647 (PA_SINK_IS_OPENED(s
->thread_info
.state
) && PA_PTR_TO_UINT(userdata
) == PA_SINK_SUSPENDED
);
1649 s
->thread_info
.state
= PA_PTR_TO_UINT(userdata
);
1651 if (s
->thread_info
.state
== PA_SINK_SUSPENDED
) {
1652 s
->thread_info
.rewind_nbytes
= 0;
1653 s
->thread_info
.rewind_requested
= FALSE
;
1656 if (suspend_change
) {
1660 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
)))
1661 if (i
->suspend_within_thread
)
1662 i
->suspend_within_thread(i
, s
->thread_info
.state
== PA_SINK_SUSPENDED
);
1668 case PA_SINK_MESSAGE_DETACH
:
1670 /* Detach all streams */
1671 pa_sink_detach_within_thread(s
);
1674 case PA_SINK_MESSAGE_ATTACH
:
1676 /* Reattach all streams */
1677 pa_sink_attach_within_thread(s
);
1680 case PA_SINK_MESSAGE_GET_REQUESTED_LATENCY
: {
1682 pa_usec_t
*usec
= userdata
;
1683 *usec
= pa_sink_get_requested_latency_within_thread(s
);
1685 if (*usec
== (pa_usec_t
) -1)
1686 *usec
= s
->thread_info
.max_latency
;
1691 case PA_SINK_MESSAGE_SET_LATENCY_RANGE
: {
1692 pa_usec_t
*r
= userdata
;
1694 pa_sink_set_latency_range_within_thread(s
, r
[0], r
[1]);
1699 case PA_SINK_MESSAGE_GET_LATENCY_RANGE
: {
1700 pa_usec_t
*r
= userdata
;
1702 r
[0] = s
->thread_info
.min_latency
;
1703 r
[1] = s
->thread_info
.max_latency
;
1708 case PA_SINK_MESSAGE_GET_MAX_REWIND
:
1710 *((size_t*) userdata
) = s
->thread_info
.max_rewind
;
1713 case PA_SINK_MESSAGE_GET_MAX_REQUEST
:
1715 *((size_t*) userdata
) = s
->thread_info
.max_request
;
1718 case PA_SINK_MESSAGE_SET_MAX_REWIND
:
1720 pa_sink_set_max_rewind_within_thread(s
, (size_t) offset
);
1723 case PA_SINK_MESSAGE_SET_MAX_REQUEST
:
1725 pa_sink_set_max_request_within_thread(s
, (size_t) offset
);
1728 case PA_SINK_MESSAGE_GET_LATENCY
:
1729 case PA_SINK_MESSAGE_MAX
:
1736 /* Called from main thread */
1737 int pa_sink_suspend_all(pa_core
*c
, pa_bool_t suspend
) {
1742 pa_core_assert_ref(c
);
1744 for (sink
= PA_SINK(pa_idxset_first(c
->sinks
, &idx
)); sink
; sink
= PA_SINK(pa_idxset_next(c
->sinks
, &idx
))) {
1747 if ((r
= pa_sink_suspend(sink
, suspend
)) < 0)
1754 /* Called from main thread */
1755 void pa_sink_detach(pa_sink
*s
) {
1756 pa_sink_assert_ref(s
);
1757 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1759 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_DETACH
, NULL
, 0, NULL
) == 0);
1762 /* Called from main thread */
1763 void pa_sink_attach(pa_sink
*s
) {
1764 pa_sink_assert_ref(s
);
1765 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1767 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_ATTACH
, NULL
, 0, NULL
) == 0);
1770 /* Called from IO thread */
1771 void pa_sink_detach_within_thread(pa_sink
*s
) {
1775 pa_sink_assert_ref(s
);
1776 pa_assert(PA_SINK_IS_LINKED(s
->thread_info
.state
));
1778 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
)))
1782 if (s
->monitor_source
)
1783 pa_source_detach_within_thread(s
->monitor_source
);
1786 /* Called from IO thread */
1787 void pa_sink_attach_within_thread(pa_sink
*s
) {
1791 pa_sink_assert_ref(s
);
1792 pa_assert(PA_SINK_IS_LINKED(s
->thread_info
.state
));
1794 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
)))
1798 if (s
->monitor_source
)
1799 pa_source_attach_within_thread(s
->monitor_source
);
1802 /* Called from IO thread */
1803 void pa_sink_request_rewind(pa_sink
*s
, size_t nbytes
) {
1804 pa_sink_assert_ref(s
);
1805 pa_assert(PA_SINK_IS_LINKED(s
->thread_info
.state
));
1807 if (s
->thread_info
.state
== PA_SINK_SUSPENDED
)
1810 if (nbytes
== (size_t) -1)
1811 nbytes
= s
->thread_info
.max_rewind
;
1813 nbytes
= PA_MIN(nbytes
, s
->thread_info
.max_rewind
);
1815 if (s
->thread_info
.rewind_requested
&&
1816 nbytes
<= s
->thread_info
.rewind_nbytes
)
1819 s
->thread_info
.rewind_nbytes
= nbytes
;
1820 s
->thread_info
.rewind_requested
= TRUE
;
1822 if (s
->request_rewind
)
1823 s
->request_rewind(s
);
1826 /* Called from IO thread */
1827 pa_usec_t
pa_sink_get_requested_latency_within_thread(pa_sink
*s
) {
1828 pa_usec_t result
= (pa_usec_t
) -1;
1831 pa_usec_t monitor_latency
;
1833 pa_sink_assert_ref(s
);
1835 if (!(s
->flags
& PA_SINK_DYNAMIC_LATENCY
))
1836 return PA_CLAMP(s
->fixed_latency
, s
->thread_info
.min_latency
, s
->thread_info
.max_latency
);
1838 if (s
->thread_info
.requested_latency_valid
)
1839 return s
->thread_info
.requested_latency
;
1841 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
)))
1843 if (i
->thread_info
.requested_sink_latency
!= (pa_usec_t
) -1 &&
1844 (result
== (pa_usec_t
) -1 || result
> i
->thread_info
.requested_sink_latency
))
1845 result
= i
->thread_info
.requested_sink_latency
;
1847 monitor_latency
= pa_source_get_requested_latency_within_thread(s
->monitor_source
);
1849 if (monitor_latency
!= (pa_usec_t
) -1 &&
1850 (result
== (pa_usec_t
) -1 || result
> monitor_latency
))
1851 result
= monitor_latency
;
1853 if (result
!= (pa_usec_t
) -1)
1854 result
= PA_CLAMP(result
, s
->thread_info
.min_latency
, s
->thread_info
.max_latency
);
1856 s
->thread_info
.requested_latency
= result
;
1857 s
->thread_info
.requested_latency_valid
= TRUE
;
1862 /* Called from main thread */
1863 pa_usec_t
pa_sink_get_requested_latency(pa_sink
*s
) {
1866 pa_sink_assert_ref(s
);
1867 pa_assert(PA_SINK_IS_LINKED(s
->state
));
1869 if (s
->state
== PA_SINK_SUSPENDED
)
1872 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_GET_REQUESTED_LATENCY
, &usec
, 0, NULL
) == 0);
1876 /* Called from IO as well as the main thread -- the latter only before the IO thread started up */
1877 void pa_sink_set_max_rewind_within_thread(pa_sink
*s
, size_t max_rewind
) {
1881 pa_sink_assert_ref(s
);
1883 if (max_rewind
== s
->thread_info
.max_rewind
)
1886 s
->thread_info
.max_rewind
= max_rewind
;
1888 if (PA_SINK_IS_LINKED(s
->thread_info
.state
)) {
1889 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
)))
1890 pa_sink_input_update_max_rewind(i
, s
->thread_info
.max_rewind
);
1893 if (s
->monitor_source
)
1894 pa_source_set_max_rewind_within_thread(s
->monitor_source
, s
->thread_info
.max_rewind
);
1897 /* Called from main thread */
1898 void pa_sink_set_max_rewind(pa_sink
*s
, size_t max_rewind
) {
1899 pa_sink_assert_ref(s
);
1901 if (PA_SINK_IS_LINKED(s
->state
))
1902 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_SET_MAX_REWIND
, NULL
, max_rewind
, NULL
) == 0);
1904 pa_sink_set_max_rewind_within_thread(s
, max_rewind
);
1907 /* Called from IO as well as the main thread -- the latter only before the IO thread started up */
1908 void pa_sink_set_max_request_within_thread(pa_sink
*s
, size_t max_request
) {
1911 pa_sink_assert_ref(s
);
1913 if (max_request
== s
->thread_info
.max_request
)
1916 s
->thread_info
.max_request
= max_request
;
1918 if (PA_SINK_IS_LINKED(s
->thread_info
.state
)) {
1921 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
)))
1922 pa_sink_input_update_max_request(i
, s
->thread_info
.max_request
);
1926 /* Called from main thread */
1927 void pa_sink_set_max_request(pa_sink
*s
, size_t max_request
) {
1928 pa_sink_assert_ref(s
);
1930 if (PA_SINK_IS_LINKED(s
->state
))
1931 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_SET_MAX_REQUEST
, NULL
, max_request
, NULL
) == 0);
1933 pa_sink_set_max_request_within_thread(s
, max_request
);
1936 /* Called from IO thread */
1937 void pa_sink_invalidate_requested_latency(pa_sink
*s
) {
1941 pa_sink_assert_ref(s
);
1943 if (!(s
->flags
& PA_SINK_DYNAMIC_LATENCY
))
1946 s
->thread_info
.requested_latency_valid
= FALSE
;
1948 if (PA_SINK_IS_LINKED(s
->thread_info
.state
)) {
1950 if (s
->update_requested_latency
)
1951 s
->update_requested_latency(s
);
1953 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
)))
1954 if (i
->update_sink_requested_latency
)
1955 i
->update_sink_requested_latency(i
);
1959 /* Called from main thread */
1960 void pa_sink_set_latency_range(pa_sink
*s
, pa_usec_t min_latency
, pa_usec_t max_latency
) {
1961 pa_sink_assert_ref(s
);
1963 /* min_latency == 0: no limit
1964 * min_latency anything else: specified limit
1966 * Similar for max_latency */
1968 if (min_latency
< ABSOLUTE_MIN_LATENCY
)
1969 min_latency
= ABSOLUTE_MIN_LATENCY
;
1971 if (max_latency
<= 0 ||
1972 max_latency
> ABSOLUTE_MAX_LATENCY
)
1973 max_latency
= ABSOLUTE_MAX_LATENCY
;
1975 pa_assert(min_latency
<= max_latency
);
1977 /* Hmm, let's see if someone forgot to set PA_SINK_DYNAMIC_LATENCY here... */
1978 pa_assert((min_latency
== ABSOLUTE_MIN_LATENCY
&&
1979 max_latency
== ABSOLUTE_MAX_LATENCY
) ||
1980 (s
->flags
& PA_SINK_DYNAMIC_LATENCY
));
1982 if (PA_SINK_IS_LINKED(s
->state
)) {
1988 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_SET_LATENCY_RANGE
, r
, 0, NULL
) == 0);
1990 pa_sink_set_latency_range_within_thread(s
, min_latency
, max_latency
);
1993 /* Called from main thread */
1994 void pa_sink_get_latency_range(pa_sink
*s
, pa_usec_t
*min_latency
, pa_usec_t
*max_latency
) {
1995 pa_sink_assert_ref(s
);
1996 pa_assert(min_latency
);
1997 pa_assert(max_latency
);
1999 if (PA_SINK_IS_LINKED(s
->state
)) {
2000 pa_usec_t r
[2] = { 0, 0 };
2002 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_GET_LATENCY_RANGE
, r
, 0, NULL
) == 0);
2004 *min_latency
= r
[0];
2005 *max_latency
= r
[1];
2007 *min_latency
= s
->thread_info
.min_latency
;
2008 *max_latency
= s
->thread_info
.max_latency
;
2012 /* Called from IO thread */
2013 void pa_sink_set_latency_range_within_thread(pa_sink
*s
, pa_usec_t min_latency
, pa_usec_t max_latency
) {
2016 pa_sink_assert_ref(s
);
2018 pa_assert(min_latency
>= ABSOLUTE_MIN_LATENCY
);
2019 pa_assert(max_latency
<= ABSOLUTE_MAX_LATENCY
);
2020 pa_assert(min_latency
<= max_latency
);
2022 /* Hmm, let's see if someone forgot to set PA_SINK_DYNAMIC_LATENCY here... */
2023 pa_assert((min_latency
== ABSOLUTE_MIN_LATENCY
&&
2024 max_latency
== ABSOLUTE_MAX_LATENCY
) ||
2025 (s
->flags
& PA_SINK_DYNAMIC_LATENCY
));
2027 s
->thread_info
.min_latency
= min_latency
;
2028 s
->thread_info
.max_latency
= max_latency
;
2030 if (PA_SINK_IS_LINKED(s
->thread_info
.state
)) {
2033 while ((i
= pa_hashmap_iterate(s
->thread_info
.inputs
, &state
, NULL
)))
2034 if (i
->update_sink_latency_range
)
2035 i
->update_sink_latency_range(i
);
2038 pa_sink_invalidate_requested_latency(s
);
2040 pa_source_set_latency_range_within_thread(s
->monitor_source
, min_latency
, max_latency
);
2043 /* Called from main context */
2044 size_t pa_sink_get_max_rewind(pa_sink
*s
) {
2046 pa_sink_assert_ref(s
);
2048 if (!PA_SINK_IS_LINKED(s
->state
))
2049 return s
->thread_info
.max_rewind
;
2051 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_GET_MAX_REWIND
, &r
, 0, NULL
) == 0);
2056 /* Called from main context */
2057 size_t pa_sink_get_max_request(pa_sink
*s
) {
2059 pa_sink_assert_ref(s
);
2061 if (!PA_SINK_IS_LINKED(s
->state
))
2062 return s
->thread_info
.max_request
;
2064 pa_assert_se(pa_asyncmsgq_send(s
->asyncmsgq
, PA_MSGOBJECT(s
), PA_SINK_MESSAGE_GET_MAX_REQUEST
, &r
, 0, NULL
) == 0);
2069 /* Called from main context */
2070 pa_bool_t
pa_device_init_icon(pa_proplist
*p
, pa_bool_t is_sink
) {
2071 const char *ff
, *c
, *t
= NULL
, *s
= "", *profile
, *bus
;
2075 if (pa_proplist_contains(p
, PA_PROP_DEVICE_ICON_NAME
))
2078 if ((ff
= pa_proplist_gets(p
, PA_PROP_DEVICE_FORM_FACTOR
))) {
2080 if (pa_streq(ff
, "microphone"))
2081 t
= "audio-input-microphone";
2082 else if (pa_streq(ff
, "webcam"))
2084 else if (pa_streq(ff
, "computer"))
2086 else if (pa_streq(ff
, "handset"))
2088 else if (pa_streq(ff
, "portable"))
2089 t
= "multimedia-player";
2090 else if (pa_streq(ff
, "tv"))
2091 t
= "video-display";
2095 if ((c
= pa_proplist_gets(p
, PA_PROP_DEVICE_CLASS
)))
2096 if (pa_streq(c
, "modem"))
2103 t
= "audio-input-microphone";
2106 if ((profile
= pa_proplist_gets(p
, PA_PROP_DEVICE_PROFILE_NAME
))) {
2107 if (strstr(profile
, "analog"))
2109 else if (strstr(profile
, "iec958"))
2111 else if (strstr(profile
, "hdmi"))
2115 bus
= pa_proplist_gets(p
, PA_PROP_DEVICE_BUS
);
2117 pa_proplist_setf(p
, PA_PROP_DEVICE_ICON_NAME
, "%s%s%s%s", t
, pa_strempty(s
), bus
? "-" : "", pa_strempty(bus
));
2122 pa_bool_t
pa_device_init_description(pa_proplist
*p
) {
2126 if (pa_proplist_contains(p
, PA_PROP_DEVICE_DESCRIPTION
))
2129 if ((s
= pa_proplist_gets(p
, PA_PROP_DEVICE_FORM_FACTOR
)))
2130 if (pa_streq(s
, "internal")) {
2131 pa_proplist_sets(p
, PA_PROP_DEVICE_DESCRIPTION
, _("Internal Audio"));
2135 if ((s
= pa_proplist_gets(p
, PA_PROP_DEVICE_CLASS
)))
2136 if (pa_streq(s
, "modem")) {
2137 pa_proplist_sets(p
, PA_PROP_DEVICE_DESCRIPTION
, _("Modem"));
2141 if ((s
= pa_proplist_gets(p
, PA_PROP_DEVICE_PRODUCT_NAME
))) {
2142 pa_proplist_sets(p
, PA_PROP_DEVICE_DESCRIPTION
, s
);