Branch data Line data Source code
1 : : /***
2 : : This file is part of PulseAudio.
3 : :
4 : : Copyright 2004-2006 Lennart Poettering
5 : :
6 : : PulseAudio is free software; you can redistribute it and/or modify
7 : : it under the terms of the GNU Lesser General Public License as published
8 : : by the Free Software Foundation; either version 2.1 of the License,
9 : : or (at your option) any later version.
10 : :
11 : : PulseAudio is distributed in the hope that it will be useful, but
12 : : WITHOUT ANY WARRANTY; without even the implied warranty of
13 : : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 : : General Public License for more details.
15 : :
16 : : You should have received a copy of the GNU Lesser General Public License
17 : : along with PulseAudio; if not, write to the Free Software
18 : : Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19 : : USA.
20 : : ***/
21 : :
22 : : #ifdef HAVE_CONFIG_H
23 : : #include <config.h>
24 : : #endif
25 : :
26 : : #include <stdio.h>
27 : : #include <stdlib.h>
28 : : #include <string.h>
29 : :
30 : : #include <pulse/utf8.h>
31 : : #include <pulse/xmalloc.h>
32 : : #include <pulse/util.h>
33 : : #include <pulse/internal.h>
34 : :
35 : : #include <pulsecore/sample-util.h>
36 : : #include <pulsecore/core-subscribe.h>
37 : : #include <pulsecore/log.h>
38 : : #include <pulsecore/namereg.h>
39 : : #include <pulsecore/core-util.h>
40 : :
41 : : #include "source-output.h"
42 : :
43 : : #define MEMBLOCKQ_MAXLENGTH (32*1024*1024)
44 : :
45 [ # # ]: 0 : PA_DEFINE_PUBLIC_CLASS(pa_source_output, pa_msgobject);
46 : :
47 : : static void source_output_free(pa_object* mo);
48 : : static void set_real_ratio(pa_source_output *o, const pa_cvolume *v);
49 : :
50 : 0 : pa_source_output_new_data* pa_source_output_new_data_init(pa_source_output_new_data *data) {
51 [ # # ]: 0 : pa_assert(data);
52 : :
53 : : pa_zero(*data);
54 : 0 : data->resample_method = PA_RESAMPLER_INVALID;
55 : 0 : data->proplist = pa_proplist_new();
56 : 0 : data->volume_writable = TRUE;
57 : :
58 : 0 : return data;
59 : : }
60 : :
61 : 0 : void pa_source_output_new_data_set_sample_spec(pa_source_output_new_data *data, const pa_sample_spec *spec) {
62 [ # # ]: 0 : pa_assert(data);
63 : :
64 [ # # ]: 0 : if ((data->sample_spec_is_set = !!spec))
65 : 0 : data->sample_spec = *spec;
66 : 0 : }
67 : :
68 : 0 : void pa_source_output_new_data_set_channel_map(pa_source_output_new_data *data, const pa_channel_map *map) {
69 [ # # ]: 0 : pa_assert(data);
70 : :
71 [ # # ]: 0 : if ((data->channel_map_is_set = !!map))
72 : 0 : data->channel_map = *map;
73 : 0 : }
74 : :
75 : 0 : pa_bool_t pa_source_output_new_data_is_passthrough(pa_source_output_new_data *data) {
76 [ # # ]: 0 : pa_assert(data);
77 : :
78 [ # # ][ # # ]: 0 : if (PA_LIKELY(data->format) && PA_UNLIKELY(!pa_format_info_is_pcm(data->format)))
79 : : return TRUE;
80 : :
81 [ # # ]: 0 : if (PA_UNLIKELY(data->flags & PA_SOURCE_OUTPUT_PASSTHROUGH))
82 : : return TRUE;
83 : :
84 : 0 : return FALSE;
85 : : }
86 : :
87 : 0 : void pa_source_output_new_data_set_volume(pa_source_output_new_data *data, const pa_cvolume *volume) {
88 [ # # ]: 0 : pa_assert(data);
89 [ # # ]: 0 : pa_assert(data->volume_writable);
90 : :
91 [ # # ]: 0 : if ((data->volume_is_set = !!volume))
92 : 0 : data->volume = *volume;
93 : 0 : }
94 : :
95 : 0 : void pa_source_output_new_data_apply_volume_factor(pa_source_output_new_data *data, const pa_cvolume *volume_factor) {
96 [ # # ]: 0 : pa_assert(data);
97 [ # # ]: 0 : pa_assert(volume_factor);
98 : :
99 [ # # ]: 0 : if (data->volume_factor_is_set)
100 : 0 : pa_sw_cvolume_multiply(&data->volume_factor, &data->volume_factor, volume_factor);
101 : : else {
102 : 0 : data->volume_factor_is_set = TRUE;
103 : 0 : data->volume_factor = *volume_factor;
104 : : }
105 : 0 : }
106 : :
107 : 0 : void pa_source_output_new_data_apply_volume_factor_source(pa_source_output_new_data *data, const pa_cvolume *volume_factor) {
108 [ # # ]: 0 : pa_assert(data);
109 [ # # ]: 0 : pa_assert(volume_factor);
110 : :
111 [ # # ]: 0 : if (data->volume_factor_source_is_set)
112 : 0 : pa_sw_cvolume_multiply(&data->volume_factor_source, &data->volume_factor_source, volume_factor);
113 : : else {
114 : 0 : data->volume_factor_source_is_set = TRUE;
115 : 0 : data->volume_factor_source = *volume_factor;
116 : : }
117 : 0 : }
118 : :
119 : 0 : void pa_source_output_new_data_set_muted(pa_source_output_new_data *data, pa_bool_t mute) {
120 [ # # ]: 0 : pa_assert(data);
121 : :
122 : 0 : data->muted_is_set = TRUE;
123 : 0 : data->muted = !!mute;
124 : 0 : }
125 : :
126 : 0 : pa_bool_t pa_source_output_new_data_set_source(pa_source_output_new_data *data, pa_source *s, pa_bool_t save) {
127 : 0 : pa_bool_t ret = TRUE;
128 : 0 : pa_idxset *formats = NULL;
129 : :
130 [ # # ]: 0 : pa_assert(data);
131 [ # # ]: 0 : pa_assert(s);
132 : :
133 [ # # ]: 0 : if (!data->req_formats) {
134 : : /* We're not working with the extended API */
135 : 0 : data->source = s;
136 : 0 : data->save_source = save;
137 : : } else {
138 : : /* Extended API: let's see if this source supports the formats the client would like */
139 : 0 : formats = pa_source_check_formats(s, data->req_formats);
140 : :
141 [ # # ][ # # ]: 0 : if (formats && !pa_idxset_isempty(formats)) {
142 : : /* Source supports at least one of the requested formats */
143 : 0 : data->source = s;
144 : 0 : data->save_source = save;
145 [ # # ]: 0 : if (data->nego_formats)
146 : 0 : pa_idxset_free(data->nego_formats, (pa_free2_cb_t) pa_format_info_free2, NULL);
147 : 0 : data->nego_formats = formats;
148 : : } else {
149 : : /* Source doesn't support any of the formats requested by the client */
150 [ # # ]: 0 : if (formats)
151 : 0 : pa_idxset_free(formats, (pa_free2_cb_t) pa_format_info_free2, NULL);
152 : : ret = FALSE;
153 : : }
154 : : }
155 : :
156 : 0 : return ret;
157 : : }
158 : :
159 : 0 : pa_bool_t pa_source_output_new_data_set_formats(pa_source_output_new_data *data, pa_idxset *formats) {
160 [ # # ]: 0 : pa_assert(data);
161 [ # # ]: 0 : pa_assert(formats);
162 : :
163 [ # # ]: 0 : if (data->req_formats)
164 : 0 : pa_idxset_free(formats, (pa_free2_cb_t) pa_format_info_free2, NULL);
165 : :
166 : 0 : data->req_formats = formats;
167 : :
168 [ # # ]: 0 : if (data->source) {
169 : : /* Trigger format negotiation */
170 : 0 : return pa_source_output_new_data_set_source(data, data->source, data->save_source);
171 : : }
172 : :
173 : : return TRUE;
174 : : }
175 : :
176 : 0 : void pa_source_output_new_data_done(pa_source_output_new_data *data) {
177 [ # # ]: 0 : pa_assert(data);
178 : :
179 [ # # ]: 0 : if (data->req_formats)
180 : 0 : pa_idxset_free(data->req_formats, (pa_free2_cb_t) pa_format_info_free2, NULL);
181 : :
182 [ # # ]: 0 : if (data->nego_formats)
183 : 0 : pa_idxset_free(data->nego_formats, (pa_free2_cb_t) pa_format_info_free2, NULL);
184 : :
185 [ # # ]: 0 : if (data->format)
186 : 0 : pa_format_info_free(data->format);
187 : :
188 : 0 : pa_proplist_free(data->proplist);
189 : 0 : }
190 : :
191 : : /* Called from main context */
192 : 0 : static void reset_callbacks(pa_source_output *o) {
193 [ # # ]: 0 : pa_assert(o);
194 : :
195 : 0 : o->push = NULL;
196 : 0 : o->process_rewind = NULL;
197 : 0 : o->update_max_rewind = NULL;
198 : 0 : o->update_source_requested_latency = NULL;
199 : 0 : o->update_source_latency_range = NULL;
200 : 0 : o->update_source_fixed_latency = NULL;
201 : 0 : o->attach = NULL;
202 : 0 : o->detach = NULL;
203 : 0 : o->suspend = NULL;
204 : 0 : o->suspend_within_thread = NULL;
205 : 0 : o->moving = NULL;
206 : 0 : o->kill = NULL;
207 : 0 : o->get_latency = NULL;
208 : 0 : o->state_change = NULL;
209 : 0 : o->may_move_to = NULL;
210 : 0 : o->send_event = NULL;
211 : 0 : o->volume_changed = NULL;
212 : 0 : o->mute_changed = NULL;
213 : 0 : }
214 : :
215 : : /* Called from main context */
216 : 0 : int pa_source_output_new(
217 : : pa_source_output**_o,
218 : : pa_core *core,
219 : : pa_source_output_new_data *data) {
220 : :
221 : : pa_source_output *o;
222 : 0 : pa_resampler *resampler = NULL;
223 : : char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
224 : : pa_channel_map original_cm;
225 : : int r;
226 : : char *pt;
227 : : pa_sample_spec ss;
228 : : pa_channel_map map;
229 : :
230 [ # # ]: 0 : pa_assert(_o);
231 [ # # ]: 0 : pa_assert(core);
232 [ # # ]: 0 : pa_assert(data);
233 [ # # ]: 0 : pa_assert_ctl_context();
234 : :
235 [ # # ]: 0 : if (data->client)
236 : 0 : pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->client->proplist);
237 : :
238 [ # # ][ # # ]: 0 : if (data->destination_source && (data->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER))
239 : 0 : data->volume_writable = FALSE;
240 : :
241 [ # # ]: 0 : if (!data->req_formats) {
242 : : /* From this point on, we want to work only with formats, and get back
243 : : * to using the sample spec and channel map after all decisions w.r.t.
244 : : * routing are complete. */
245 : 0 : pa_idxset *tmp = pa_idxset_new(NULL, NULL);
246 [ # # ]: 0 : pa_format_info *f = pa_format_info_from_sample_spec(&data->sample_spec,
247 : 0 : data->channel_map_is_set ? &data->channel_map : NULL);
248 : 0 : pa_idxset_put(tmp, f, NULL);
249 : 0 : pa_source_output_new_data_set_formats(data, tmp);
250 : : }
251 : :
252 [ # # ]: 0 : if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], data)) < 0)
253 : : return r;
254 : :
255 [ # # ][ # # ]: 0 : pa_return_val_if_fail(!data->driver || pa_utf8_valid(data->driver), -PA_ERR_INVALID);
256 : :
257 [ # # ]: 0 : if (!data->source) {
258 : 0 : pa_source *source = pa_namereg_get(core, NULL, PA_NAMEREG_SOURCE);
259 [ # # ]: 0 : pa_return_val_if_fail(source, -PA_ERR_NOENTITY);
260 : 0 : pa_source_output_new_data_set_source(data, source, FALSE);
261 : : }
262 : :
263 : : /* Routing's done, we have a source. Now let's fix the format and set up the
264 : : * sample spec */
265 : :
266 : : /* If something didn't pick a format for us, pick the top-most format since
267 : : * we assume this is sorted in priority order */
268 [ # # ][ # # ]: 0 : if (!data->format && data->nego_formats && !pa_idxset_isempty(data->nego_formats))
[ # # ]
269 : 0 : data->format = pa_format_info_copy(pa_idxset_first(data->nego_formats, NULL));
270 : :
271 [ # # ]: 0 : pa_return_val_if_fail(data->format, -PA_ERR_NOTSUPPORTED);
272 : :
273 : : /* Now populate the sample spec and format according to the final
274 : : * format that we've negotiated */
275 [ # # ]: 0 : pa_return_val_if_fail(pa_format_info_to_sample_spec(data->format, &ss, &map) == 0, -PA_ERR_INVALID);
276 : 0 : pa_source_output_new_data_set_sample_spec(data, &ss);
277 [ # # ][ # # ]: 0 : if (pa_format_info_is_pcm(data->format) && pa_channel_map_valid(&map))
278 : 0 : pa_source_output_new_data_set_channel_map(data, &map);
279 : :
280 [ # # ]: 0 : pa_return_val_if_fail(PA_SOURCE_IS_LINKED(pa_source_get_state(data->source)), -PA_ERR_BADSTATE);
281 [ # # ][ # # ]: 0 : pa_return_val_if_fail(!data->direct_on_input || data->direct_on_input->sink == data->source->monitor_of, -PA_ERR_INVALID);
282 : :
283 [ # # ]: 0 : if (!data->sample_spec_is_set)
284 : 0 : data->sample_spec = data->source->sample_spec;
285 : :
286 [ # # ]: 0 : pa_return_val_if_fail(pa_sample_spec_valid(&data->sample_spec), -PA_ERR_INVALID);
287 : :
288 [ # # ]: 0 : if (!data->channel_map_is_set) {
289 [ # # ]: 0 : if (pa_channel_map_compatible(&data->source->channel_map, &data->sample_spec))
290 : 0 : data->channel_map = data->source->channel_map;
291 : : else
292 : 0 : pa_channel_map_init_extend(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
293 : : }
294 : :
295 [ # # ]: 0 : pa_return_val_if_fail(pa_channel_map_compatible(&data->channel_map, &data->sample_spec), -PA_ERR_INVALID);
296 : :
297 : : /* Don't restore (or save) stream volume for passthrough streams and
298 : : * prevent attenuation/gain */
299 [ # # ]: 0 : if (pa_source_output_new_data_is_passthrough(data)) {
300 : 0 : data->volume_is_set = TRUE;
301 : 0 : pa_cvolume_reset(&data->volume, data->sample_spec.channels);
302 : 0 : data->volume_is_absolute = TRUE;
303 : 0 : data->save_volume = FALSE;
304 : : }
305 : :
306 [ # # ]: 0 : if (!data->volume_is_set) {
307 : 0 : pa_cvolume_reset(&data->volume, data->sample_spec.channels);
308 : 0 : data->volume_is_absolute = FALSE;
309 : 0 : data->save_volume = FALSE;
310 : : }
311 : :
312 [ # # ]: 0 : pa_return_val_if_fail(pa_cvolume_compatible(&data->volume, &data->sample_spec), -PA_ERR_INVALID);
313 : :
314 [ # # ]: 0 : if (!data->volume_factor_is_set)
315 : 0 : pa_cvolume_reset(&data->volume_factor, data->sample_spec.channels);
316 : :
317 [ # # ]: 0 : pa_return_val_if_fail(pa_cvolume_compatible(&data->volume_factor, &data->sample_spec), -PA_ERR_INVALID);
318 : :
319 [ # # ]: 0 : if (!data->volume_factor_source_is_set)
320 : 0 : pa_cvolume_reset(&data->volume_factor_source, data->source->sample_spec.channels);
321 : :
322 [ # # ]: 0 : pa_return_val_if_fail(pa_cvolume_compatible(&data->volume_factor_source, &data->source->sample_spec), -PA_ERR_INVALID);
323 : :
324 [ # # ]: 0 : if (!data->muted_is_set)
325 : 0 : data->muted = FALSE;
326 : :
327 [ # # ]: 0 : if (data->flags & PA_SOURCE_OUTPUT_FIX_FORMAT)
328 : 0 : data->sample_spec.format = data->source->sample_spec.format;
329 : :
330 [ # # ]: 0 : if (data->flags & PA_SOURCE_OUTPUT_FIX_RATE)
331 : 0 : data->sample_spec.rate = data->source->sample_spec.rate;
332 : :
333 : 0 : original_cm = data->channel_map;
334 : :
335 [ # # ]: 0 : if (data->flags & PA_SOURCE_OUTPUT_FIX_CHANNELS) {
336 : 0 : data->sample_spec.channels = data->source->sample_spec.channels;
337 : 0 : data->channel_map = data->source->channel_map;
338 : : }
339 : :
340 [ # # ]: 0 : pa_assert(pa_sample_spec_valid(&data->sample_spec));
341 [ # # ]: 0 : pa_assert(pa_channel_map_valid(&data->channel_map));
342 : :
343 : : /* Due to the fixing of the sample spec the volume might not match anymore */
344 : 0 : pa_cvolume_remap(&data->volume, &original_cm, &data->channel_map);
345 : :
346 [ # # # # ]: 0 : if (!(data->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) &&
347 : 0 : !pa_sample_spec_equal(&data->sample_spec, &data->source->sample_spec)){
348 : : /* try to change source rate. This is done before the FIXATE hook since
349 : : module-suspend-on-idle can resume a source */
350 : :
351 : 0 : pa_log_info("Trying to change sample rate");
352 [ # # ]: 0 : if (pa_source_update_rate(data->source, data->sample_spec.rate, pa_source_output_new_data_is_passthrough(data)) == TRUE)
353 : 0 : pa_log_info("Rate changed to %u Hz",
354 : : data->source->sample_spec.rate);
355 : : else
356 : 0 : pa_log_info("Resampling enabled to %u Hz", data->source->sample_spec.rate);
357 : : }
358 : :
359 [ # # ]: 0 : if (data->resample_method == PA_RESAMPLER_INVALID)
360 : 0 : data->resample_method = core->resample_method;
361 : :
362 [ # # ]: 0 : pa_return_val_if_fail(data->resample_method < PA_RESAMPLER_MAX, -PA_ERR_INVALID);
363 : :
364 [ # # ]: 0 : if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], data)) < 0)
365 : : return r;
366 : :
367 [ # # ][ # # ]: 0 : if ((data->flags & PA_SOURCE_OUTPUT_NO_CREATE_ON_SUSPEND) &&
368 : 0 : pa_source_get_state(data->source) == PA_SOURCE_SUSPENDED) {
369 : 0 : pa_log("Failed to create source output: source is suspended.");
370 : 0 : return -PA_ERR_BADSTATE;
371 : : }
372 : :
373 [ # # ]: 0 : if (pa_idxset_size(data->source->outputs) >= PA_MAX_OUTPUTS_PER_SOURCE) {
374 : 0 : pa_log("Failed to create source output: too many outputs per source.");
375 : 0 : return -PA_ERR_TOOLARGE;
376 : : }
377 : :
378 [ # # # # ]: 0 : if ((data->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ||
379 [ # # ]: 0 : !pa_sample_spec_equal(&data->sample_spec, &data->source->sample_spec) ||
380 : 0 : !pa_channel_map_equal(&data->channel_map, &data->source->channel_map)) {
381 : :
382 [ # # ]: 0 : if (!pa_source_output_new_data_is_passthrough(data)) /* no resampler for passthrough content */
383 [ # # ]: 0 : if (!(resampler = pa_resampler_new(
384 : : core->mempool,
385 : 0 : &data->source->sample_spec, &data->source->channel_map,
386 : 0 : &data->sample_spec, &data->channel_map,
387 : : data->resample_method,
388 : 0 : ((data->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
389 [ # # ]: 0 : ((data->flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
390 [ # # ][ # # ]: 0 : (core->disable_remixing || (data->flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) |
391 [ # # ]: 0 : (core->disable_lfe_remixing ? PA_RESAMPLER_NO_LFE : 0)))) {
392 : 0 : pa_log_warn("Unsupported resampling operation.");
393 : 0 : return -PA_ERR_NOTSUPPORTED;
394 : : }
395 : : }
396 : :
397 : 0 : o = pa_msgobject_new(pa_source_output);
398 : 0 : o->parent.parent.free = source_output_free;
399 : 0 : o->parent.process_msg = pa_source_output_process_msg;
400 : :
401 : 0 : o->core = core;
402 : 0 : o->state = PA_SOURCE_OUTPUT_INIT;
403 : 0 : o->flags = data->flags;
404 : 0 : o->proplist = pa_proplist_copy(data->proplist);
405 : 0 : o->driver = pa_xstrdup(pa_path_get_filename(data->driver));
406 : 0 : o->module = data->module;
407 : 0 : o->source = data->source;
408 : 0 : o->destination_source = data->destination_source;
409 : 0 : o->client = data->client;
410 : :
411 : 0 : o->requested_resample_method = data->resample_method;
412 [ # # ]: 0 : o->actual_resample_method = resampler ? pa_resampler_get_method(resampler) : PA_RESAMPLER_INVALID;
413 : 0 : o->sample_spec = data->sample_spec;
414 : 0 : o->channel_map = data->channel_map;
415 : 0 : o->format = pa_format_info_copy(data->format);
416 : :
417 [ # # ][ # # ]: 0 : if (!data->volume_is_absolute && pa_source_flat_volume_enabled(o->source)) {
418 : : pa_cvolume remapped;
419 : :
420 : : /* When the 'absolute' bool is not set then we'll treat the volume
421 : : * as relative to the source volume even in flat volume mode */
422 : 0 : remapped = data->source->reference_volume;
423 : 0 : pa_cvolume_remap(&remapped, &data->source->channel_map, &data->channel_map);
424 : 0 : pa_sw_cvolume_multiply(&o->volume, &data->volume, &remapped);
425 : : } else
426 : 0 : o->volume = data->volume;
427 : :
428 : 0 : o->volume_factor = data->volume_factor;
429 : 0 : o->volume_factor_source = data->volume_factor_source;
430 : 0 : o->real_ratio = o->reference_ratio = data->volume;
431 : 0 : pa_cvolume_reset(&o->soft_volume, o->sample_spec.channels);
432 : 0 : pa_cvolume_reset(&o->real_ratio, o->sample_spec.channels);
433 : 0 : o->volume_writable = data->volume_writable;
434 : 0 : o->save_volume = data->save_volume;
435 : 0 : o->save_source = data->save_source;
436 : 0 : o->save_muted = data->save_muted;
437 : :
438 : 0 : o->muted = data->muted;
439 : :
440 : 0 : o->direct_on_input = data->direct_on_input;
441 : :
442 : 0 : reset_callbacks(o);
443 : 0 : o->userdata = NULL;
444 : :
445 : 0 : o->thread_info.state = o->state;
446 : 0 : o->thread_info.attached = FALSE;
447 : 0 : o->thread_info.sample_spec = o->sample_spec;
448 : 0 : o->thread_info.resampler = resampler;
449 : 0 : o->thread_info.soft_volume = o->soft_volume;
450 : 0 : o->thread_info.muted = o->muted;
451 : 0 : o->thread_info.requested_source_latency = (pa_usec_t) -1;
452 : 0 : o->thread_info.direct_on_input = o->direct_on_input;
453 : :
454 : 0 : o->thread_info.delay_memblockq = pa_memblockq_new(
455 : : "source output delay_memblockq",
456 : : 0,
457 : : MEMBLOCKQ_MAXLENGTH,
458 : : 0,
459 : 0 : &o->source->sample_spec,
460 : : 0,
461 : : 1,
462 : : 0,
463 : 0 : &o->source->silence);
464 : :
465 [ # # ]: 0 : pa_assert_se(pa_idxset_put(core->source_outputs, o, &o->index) == 0);
466 [ # # ]: 0 : pa_assert_se(pa_idxset_put(o->source->outputs, pa_source_output_ref(o), NULL) == 0);
467 : :
468 [ # # ]: 0 : if (o->client)
469 [ # # ]: 0 : pa_assert_se(pa_idxset_put(o->client->source_outputs, o, NULL) >= 0);
470 : :
471 [ # # ]: 0 : if (o->direct_on_input)
472 [ # # ]: 0 : pa_assert_se(pa_idxset_put(o->direct_on_input->direct_outputs, o, NULL) == 0);
473 : :
474 : 0 : pt = pa_proplist_to_string_sep(o->proplist, "\n ");
475 : 0 : pa_log_info("Created output %u \"%s\" on %s with sample spec %s and channel map %s\n %s",
476 : : o->index,
477 : : pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_MEDIA_NAME)),
478 : : o->source->name,
479 : : pa_sample_spec_snprint(st, sizeof(st), &o->sample_spec),
480 : : pa_channel_map_snprint(cm, sizeof(cm), &o->channel_map),
481 : : pt);
482 : 0 : pa_xfree(pt);
483 : :
484 : : /* Don't forget to call pa_source_output_put! */
485 : :
486 : 0 : *_o = o;
487 : 0 : return 0;
488 : : }
489 : :
490 : : /* Called from main context */
491 : 0 : static void update_n_corked(pa_source_output *o, pa_source_output_state_t state) {
492 [ # # ]: 0 : pa_assert(o);
493 [ # # ]: 0 : pa_assert_ctl_context();
494 : :
495 [ # # ]: 0 : if (!o->source)
496 : 0 : return;
497 : :
498 [ # # ]: 0 : if (o->state == PA_SOURCE_OUTPUT_CORKED && state != PA_SOURCE_OUTPUT_CORKED)
499 [ # # ]: 0 : pa_assert_se(o->source->n_corked -- >= 1);
500 [ # # ]: 0 : else if (o->state != PA_SOURCE_OUTPUT_CORKED && state == PA_SOURCE_OUTPUT_CORKED)
501 : 0 : o->source->n_corked++;
502 : : }
503 : :
504 : : /* Called from main context */
505 : 0 : static void source_output_set_state(pa_source_output *o, pa_source_output_state_t state) {
506 : :
507 [ # # ]: 0 : pa_assert(o);
508 [ # # ]: 0 : pa_assert_ctl_context();
509 : :
510 [ # # ]: 0 : if (o->state == state)
511 : 0 : return;
512 : :
513 [ # # ]: 0 : if (o->state == PA_SOURCE_OUTPUT_CORKED && state == PA_SOURCE_OUTPUT_RUNNING && pa_source_used_by(o->source) == 0 &&
[ # # # # ]
514 : 0 : !pa_sample_spec_equal(&o->sample_spec, &o->source->sample_spec)) {
515 : : /* We were uncorked and the source was not playing anything -- let's try
516 : : * to update the sample rate to avoid resampling */
517 : 0 : pa_source_update_rate(o->source, o->sample_spec.rate, pa_source_output_is_passthrough(o));
518 : : }
519 : :
520 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) == 0);
521 : :
522 : 0 : update_n_corked(o, state);
523 : 0 : o->state = state;
524 : :
525 [ # # ]: 0 : if (state != PA_SOURCE_OUTPUT_UNLINKED) {
526 : 0 : pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED], o);
527 : :
528 [ # # ]: 0 : if (PA_SOURCE_OUTPUT_IS_LINKED(state))
529 : 0 : pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
530 : : }
531 : :
532 : 0 : pa_source_update_status(o->source);
533 : : }
534 : :
535 : : /* Called from main context */
536 : 0 : void pa_source_output_unlink(pa_source_output*o) {
537 : : pa_bool_t linked;
538 [ # # ]: 0 : pa_assert(o);
539 [ # # ]: 0 : pa_assert_ctl_context();
540 : :
541 : : /* See pa_sink_unlink() for a couple of comments how this function
542 : : * works */
543 : :
544 : 0 : pa_source_output_ref(o);
545 : :
546 : 0 : linked = PA_SOURCE_OUTPUT_IS_LINKED(o->state);
547 : :
548 [ # # ]: 0 : if (linked)
549 : 0 : pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK], o);
550 : :
551 [ # # ]: 0 : if (o->direct_on_input)
552 : 0 : pa_idxset_remove_by_data(o->direct_on_input->direct_outputs, o, NULL);
553 : :
554 : 0 : pa_idxset_remove_by_data(o->core->source_outputs, o, NULL);
555 : :
556 [ # # ]: 0 : if (o->source)
557 [ # # ]: 0 : if (pa_idxset_remove_by_data(o->source->outputs, o, NULL))
558 : 0 : pa_source_output_unref(o);
559 : :
560 [ # # ]: 0 : if (o->client)
561 : 0 : pa_idxset_remove_by_data(o->client->source_outputs, o, NULL);
562 : :
563 : 0 : update_n_corked(o, PA_SOURCE_OUTPUT_UNLINKED);
564 : 0 : o->state = PA_SOURCE_OUTPUT_UNLINKED;
565 : :
566 [ # # ][ # # ]: 0 : if (linked && o->source) {
567 [ # # ]: 0 : if (pa_source_output_is_passthrough(o))
568 : 0 : pa_source_leave_passthrough(o->source);
569 : :
570 : : /* We might need to update the source's volume if we are in flat volume mode. */
571 [ # # ]: 0 : if (pa_source_flat_volume_enabled(o->source))
572 : 0 : pa_source_set_volume(o->source, NULL, FALSE, FALSE);
573 : :
574 [ # # ]: 0 : if (o->source->asyncmsgq)
575 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL) == 0);
576 : : }
577 : :
578 : 0 : reset_callbacks(o);
579 : :
580 [ # # ]: 0 : if (linked) {
581 : 0 : pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_REMOVE, o->index);
582 : 0 : pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST], o);
583 : : }
584 : :
585 [ # # ]: 0 : if (o->source) {
586 [ # # ]: 0 : if (PA_SOURCE_IS_LINKED(pa_source_get_state(o->source)))
587 : 0 : pa_source_update_status(o->source);
588 : :
589 : 0 : o->source = NULL;
590 : : }
591 : :
592 : 0 : pa_core_maybe_vacuum(o->core);
593 : :
594 : 0 : pa_source_output_unref(o);
595 : 0 : }
596 : :
597 : : /* Called from main context */
598 : 0 : static void source_output_free(pa_object* mo) {
599 : 0 : pa_source_output *o = PA_SOURCE_OUTPUT(mo);
600 : :
601 [ # # ]: 0 : pa_assert(o);
602 [ # # ]: 0 : pa_assert_ctl_context();
603 [ # # ]: 0 : pa_assert(pa_source_output_refcnt(o) == 0);
604 : :
605 [ # # ]: 0 : if (PA_SOURCE_OUTPUT_IS_LINKED(o->state))
606 : 0 : pa_source_output_unlink(o);
607 : :
608 : 0 : pa_log_info("Freeing output %u \"%s\"", o->index, pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_MEDIA_NAME)));
609 : :
610 [ # # ]: 0 : if (o->thread_info.delay_memblockq)
611 : 0 : pa_memblockq_free(o->thread_info.delay_memblockq);
612 : :
613 [ # # ]: 0 : if (o->thread_info.resampler)
614 : 0 : pa_resampler_free(o->thread_info.resampler);
615 : :
616 [ # # ]: 0 : if (o->format)
617 : 0 : pa_format_info_free(o->format);
618 : :
619 [ # # ]: 0 : if (o->proplist)
620 : 0 : pa_proplist_free(o->proplist);
621 : :
622 : 0 : pa_xfree(o->driver);
623 : 0 : pa_xfree(o);
624 : 0 : }
625 : :
626 : : /* Called from main context */
627 : 0 : void pa_source_output_put(pa_source_output *o) {
628 : : pa_source_output_state_t state;
629 : :
630 : 0 : pa_source_output_assert_ref(o);
631 [ # # ]: 0 : pa_assert_ctl_context();
632 : :
633 [ # # ]: 0 : pa_assert(o->state == PA_SOURCE_OUTPUT_INIT);
634 : :
635 : : /* The following fields must be initialized properly */
636 [ # # ]: 0 : pa_assert(o->push);
637 [ # # ]: 0 : pa_assert(o->kill);
638 : :
639 [ # # ]: 0 : state = o->flags & PA_SOURCE_OUTPUT_START_CORKED ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING;
640 : :
641 : 0 : update_n_corked(o, state);
642 : 0 : o->state = state;
643 : :
644 : : /* We might need to update the source's volume if we are in flat volume mode. */
645 [ # # ]: 0 : if (pa_source_flat_volume_enabled(o->source))
646 : 0 : pa_source_set_volume(o->source, NULL, FALSE, o->save_volume);
647 : : else {
648 [ # # ][ # # ]: 0 : if (o->destination_source && (o->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)) {
649 [ # # ]: 0 : pa_assert(pa_cvolume_is_norm(&o->volume));
650 [ # # ]: 0 : pa_assert(pa_cvolume_is_norm(&o->reference_ratio));
651 : : }
652 : :
653 : 0 : set_real_ratio(o, &o->volume);
654 : : }
655 : :
656 [ # # ]: 0 : if (pa_source_output_is_passthrough(o))
657 : 0 : pa_source_enter_passthrough(o->source);
658 : :
659 : 0 : o->thread_info.soft_volume = o->soft_volume;
660 : 0 : o->thread_info.muted = o->muted;
661 : :
662 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_ADD_OUTPUT, o, 0, NULL) == 0);
663 : :
664 : 0 : pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW, o->index);
665 : 0 : pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT], o);
666 : :
667 : 0 : pa_source_update_status(o->source);
668 : 0 : }
669 : :
670 : : /* Called from main context */
671 : 0 : void pa_source_output_kill(pa_source_output*o) {
672 : 0 : pa_source_output_assert_ref(o);
673 [ # # ]: 0 : pa_assert_ctl_context();
674 [ # # ]: 0 : pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
675 : :
676 : 0 : o->kill(o);
677 : 0 : }
678 : :
679 : : /* Called from main context */
680 : 0 : pa_usec_t pa_source_output_get_latency(pa_source_output *o, pa_usec_t *source_latency) {
681 : 0 : pa_usec_t r[2] = { 0, 0 };
682 : :
683 : 0 : pa_source_output_assert_ref(o);
684 [ # # ]: 0 : pa_assert_ctl_context();
685 [ # # ]: 0 : pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
686 : :
687 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY, r, 0, NULL) == 0);
688 : :
689 [ # # ]: 0 : if (o->get_latency)
690 : 0 : r[0] += o->get_latency(o);
691 : :
692 [ # # ]: 0 : if (source_latency)
693 : 0 : *source_latency = r[1];
694 : :
695 : 0 : return r[0];
696 : : }
697 : :
698 : : /* Called from thread context */
699 : 0 : void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) {
700 : : pa_bool_t need_volume_factor_source;
701 : : pa_bool_t volume_is_norm;
702 : : size_t length;
703 : 0 : size_t limit, mbs = 0;
704 : :
705 : 0 : pa_source_output_assert_ref(o);
706 [ # # ][ # # ]: 0 : pa_source_output_assert_io_context(o);
707 [ # # ]: 0 : pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state));
708 [ # # ]: 0 : pa_assert(chunk);
709 [ # # ]: 0 : pa_assert(pa_frame_aligned(chunk->length, &o->source->sample_spec));
710 : :
711 [ # # ][ # # ]: 0 : if (!o->push || o->thread_info.state == PA_SOURCE_OUTPUT_CORKED)
712 : 0 : return;
713 : :
714 [ # # ]: 0 : pa_assert(o->thread_info.state == PA_SOURCE_OUTPUT_RUNNING);
715 : :
716 [ # # ]: 0 : if (pa_memblockq_push(o->thread_info.delay_memblockq, chunk) < 0) {
717 : 0 : pa_log_debug("Delay queue overflow!");
718 : 0 : pa_memblockq_seek(o->thread_info.delay_memblockq, (int64_t) chunk->length, PA_SEEK_RELATIVE, TRUE);
719 : : }
720 : :
721 [ # # ]: 0 : limit = o->process_rewind ? 0 : o->source->thread_info.max_rewind;
722 : :
723 [ # # ][ # # ]: 0 : volume_is_norm = pa_cvolume_is_norm(&o->thread_info.soft_volume) && !o->thread_info.muted;
724 : 0 : need_volume_factor_source = !pa_cvolume_is_norm(&o->volume_factor_source);
725 : :
726 [ # # ][ # # ]: 0 : if (limit > 0 && o->source->monitor_of) {
727 : : pa_usec_t latency;
728 : : size_t n;
729 : :
730 : : /* Hmm, check the latency for knowing how much of the buffered
731 : : * data is actually still unplayed and might hence still
732 : : * change. This is suboptimal. Ideally we'd have a call like
733 : : * pa_sink_get_changeable_size() or so that tells us how much
734 : : * of the queued data is actually still changeable. Hence
735 : : * FIXME! */
736 : :
737 : 0 : latency = pa_sink_get_latency_within_thread(o->source->monitor_of);
738 : :
739 : 0 : n = pa_usec_to_bytes(latency, &o->source->sample_spec);
740 : :
741 [ # # ]: 0 : if (n < limit)
742 : 0 : limit = n;
743 : : }
744 : :
745 : : /* Implement the delay queue */
746 [ # # ]: 0 : while ((length = pa_memblockq_get_length(o->thread_info.delay_memblockq)) > limit) {
747 : : pa_memchunk qchunk;
748 : 0 : pa_bool_t nvfs = need_volume_factor_source;
749 : :
750 : 0 : length -= limit;
751 : :
752 [ # # ]: 0 : pa_assert_se(pa_memblockq_peek(o->thread_info.delay_memblockq, &qchunk) >= 0);
753 : :
754 [ # # ]: 0 : if (qchunk.length > length)
755 : 0 : qchunk.length = length;
756 : :
757 [ # # ]: 0 : pa_assert(qchunk.length > 0);
758 : :
759 : : /* It might be necessary to adjust the volume here */
760 [ # # ]: 0 : if (!volume_is_norm) {
761 : 0 : pa_memchunk_make_writable(&qchunk, 0);
762 : :
763 [ # # ]: 0 : if (o->thread_info.muted) {
764 : 0 : pa_silence_memchunk(&qchunk, &o->source->sample_spec);
765 : 0 : nvfs = FALSE;
766 : :
767 [ # # ][ # # ]: 0 : } else if (!o->thread_info.resampler && nvfs) {
768 : : pa_cvolume v;
769 : :
770 : : /* If we don't need a resampler we can merge the
771 : : * post and the pre volume adjustment into one */
772 : :
773 : 0 : pa_sw_cvolume_multiply(&v, &o->thread_info.soft_volume, &o->volume_factor_source);
774 : 0 : pa_volume_memchunk(&qchunk, &o->source->sample_spec, &v);
775 : 0 : nvfs = FALSE;
776 : :
777 : : } else
778 : 0 : pa_volume_memchunk(&qchunk, &o->source->sample_spec, &o->thread_info.soft_volume);
779 : : }
780 : :
781 [ # # ]: 0 : if (!o->thread_info.resampler) {
782 [ # # ]: 0 : if (nvfs) {
783 : 0 : pa_memchunk_make_writable(&qchunk, 0);
784 : 0 : pa_volume_memchunk(&qchunk, &o->thread_info.sample_spec, &o->volume_factor_source);
785 : : }
786 : :
787 : 0 : o->push(o, &qchunk);
788 : : } else {
789 : : pa_memchunk rchunk;
790 : :
791 [ # # ]: 0 : if (mbs == 0)
792 : 0 : mbs = pa_resampler_max_block_size(o->thread_info.resampler);
793 : :
794 [ # # ]: 0 : if (qchunk.length > mbs)
795 : 0 : qchunk.length = mbs;
796 : :
797 : 0 : pa_resampler_run(o->thread_info.resampler, &qchunk, &rchunk);
798 : :
799 [ # # ]: 0 : if (rchunk.length > 0) {
800 [ # # ]: 0 : if (nvfs) {
801 : 0 : pa_memchunk_make_writable(&rchunk, 0);
802 : 0 : pa_volume_memchunk(&rchunk, &o->thread_info.sample_spec, &o->volume_factor_source);
803 : : }
804 : :
805 : 0 : o->push(o, &rchunk);
806 : : }
807 : :
808 [ # # ]: 0 : if (rchunk.memblock)
809 : 0 : pa_memblock_unref(rchunk.memblock);
810 : : }
811 : :
812 : 0 : pa_memblock_unref(qchunk.memblock);
813 : 0 : pa_memblockq_drop(o->thread_info.delay_memblockq, qchunk.length);
814 : : }
815 : : }
816 : :
817 : : /* Called from thread context */
818 : 0 : void pa_source_output_process_rewind(pa_source_output *o, size_t nbytes /* in source sample spec */) {
819 : :
820 : 0 : pa_source_output_assert_ref(o);
821 [ # # ][ # # ]: 0 : pa_source_output_assert_io_context(o);
822 [ # # ]: 0 : pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state));
823 [ # # ]: 0 : pa_assert(pa_frame_aligned(nbytes, &o->source->sample_spec));
824 : :
825 [ # # ]: 0 : if (nbytes <= 0)
826 : 0 : return;
827 : :
828 [ # # ]: 0 : if (o->process_rewind) {
829 [ # # ]: 0 : pa_assert(pa_memblockq_get_length(o->thread_info.delay_memblockq) == 0);
830 : :
831 [ # # ]: 0 : if (o->thread_info.resampler)
832 : 0 : nbytes = pa_resampler_result(o->thread_info.resampler, nbytes);
833 : :
834 : 0 : pa_log_debug("Have to rewind %lu bytes on implementor.", (unsigned long) nbytes);
835 : :
836 [ # # ]: 0 : if (nbytes > 0)
837 : 0 : o->process_rewind(o, nbytes);
838 : :
839 [ # # ]: 0 : if (o->thread_info.resampler)
840 : 0 : pa_resampler_reset(o->thread_info.resampler);
841 : :
842 : : } else
843 : 0 : pa_memblockq_rewind(o->thread_info.delay_memblockq, nbytes);
844 : : }
845 : :
846 : : /* Called from thread context */
847 : 0 : size_t pa_source_output_get_max_rewind(pa_source_output *o) {
848 : 0 : pa_source_output_assert_ref(o);
849 [ # # ][ # # ]: 0 : pa_source_output_assert_io_context(o);
850 : :
851 [ # # ]: 0 : return o->thread_info.resampler ? pa_resampler_request(o->thread_info.resampler, o->source->thread_info.max_rewind) : o->source->thread_info.max_rewind;
852 : : }
853 : :
854 : : /* Called from thread context */
855 : 0 : void pa_source_output_update_max_rewind(pa_source_output *o, size_t nbytes /* in the source's sample spec */) {
856 : 0 : pa_source_output_assert_ref(o);
857 [ # # ][ # # ]: 0 : pa_source_output_assert_io_context(o);
858 [ # # ]: 0 : pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state));
859 [ # # ]: 0 : pa_assert(pa_frame_aligned(nbytes, &o->source->sample_spec));
860 : :
861 [ # # ]: 0 : if (o->update_max_rewind)
862 [ # # ]: 0 : o->update_max_rewind(o, o->thread_info.resampler ? pa_resampler_result(o->thread_info.resampler, nbytes) : nbytes);
863 : 0 : }
864 : :
865 : : /* Called from thread context */
866 : 0 : pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output *o, pa_usec_t usec) {
867 : 0 : pa_source_output_assert_ref(o);
868 [ # # ][ # # ]: 0 : pa_source_output_assert_io_context(o);
869 : :
870 [ # # ]: 0 : if (!(o->source->flags & PA_SOURCE_DYNAMIC_LATENCY))
871 : 0 : usec = o->source->thread_info.fixed_latency;
872 : :
873 [ # # ]: 0 : if (usec != (pa_usec_t) -1)
874 [ # # ]: 0 : usec = PA_CLAMP(usec, o->source->thread_info.min_latency, o->source->thread_info.max_latency);
875 : :
876 : 0 : o->thread_info.requested_source_latency = usec;
877 : 0 : pa_source_invalidate_requested_latency(o->source, TRUE);
878 : :
879 : 0 : return usec;
880 : : }
881 : :
882 : : /* Called from main context */
883 : 0 : pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t usec) {
884 : 0 : pa_source_output_assert_ref(o);
885 [ # # ]: 0 : pa_assert_ctl_context();
886 : :
887 [ # # ][ # # ]: 0 : if (PA_SOURCE_OUTPUT_IS_LINKED(o->state) && o->source) {
888 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
889 : 0 : return usec;
890 : : }
891 : :
892 : : /* If this source output is not realized yet or is being moved, we
893 : : * have to touch the thread info data directly */
894 : :
895 [ # # ]: 0 : if (o->source) {
896 [ # # ]: 0 : if (!(o->source->flags & PA_SOURCE_DYNAMIC_LATENCY))
897 : 0 : usec = pa_source_get_fixed_latency(o->source);
898 : :
899 [ # # ]: 0 : if (usec != (pa_usec_t) -1) {
900 : : pa_usec_t min_latency, max_latency;
901 : 0 : pa_source_get_latency_range(o->source, &min_latency, &max_latency);
902 [ # # ]: 0 : usec = PA_CLAMP(usec, min_latency, max_latency);
903 : : }
904 : : }
905 : :
906 : 0 : o->thread_info.requested_source_latency = usec;
907 : :
908 : 0 : return usec;
909 : : }
910 : :
911 : : /* Called from main context */
912 : 0 : pa_usec_t pa_source_output_get_requested_latency(pa_source_output *o) {
913 : 0 : pa_source_output_assert_ref(o);
914 [ # # ]: 0 : pa_assert_ctl_context();
915 : :
916 [ # # ][ # # ]: 0 : if (PA_SOURCE_OUTPUT_IS_LINKED(o->state) && o->source) {
917 : 0 : pa_usec_t usec = 0;
918 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
919 : 0 : return usec;
920 : : }
921 : :
922 : : /* If this source output is not realized yet or is being moved, we
923 : : * have to touch the thread info data directly */
924 : :
925 : 0 : return o->thread_info.requested_source_latency;
926 : : }
927 : :
928 : : /* Called from main context */
929 : 0 : void pa_source_output_set_volume(pa_source_output *o, const pa_cvolume *volume, pa_bool_t save, pa_bool_t absolute) {
930 : : pa_cvolume v;
931 : :
932 : 0 : pa_source_output_assert_ref(o);
933 [ # # ]: 0 : pa_assert_ctl_context();
934 [ # # ]: 0 : pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
935 [ # # ]: 0 : pa_assert(volume);
936 [ # # ]: 0 : pa_assert(pa_cvolume_valid(volume));
937 [ # # ][ # # ]: 0 : pa_assert(volume->channels == 1 || pa_cvolume_compatible(volume, &o->sample_spec));
938 [ # # ]: 0 : pa_assert(o->volume_writable);
939 : :
940 [ # # ][ # # ]: 0 : if (!absolute && pa_source_flat_volume_enabled(o->source)) {
941 : 0 : v = o->source->reference_volume;
942 : 0 : pa_cvolume_remap(&v, &o->source->channel_map, &o->channel_map);
943 : :
944 [ # # ]: 0 : if (pa_cvolume_compatible(volume, &o->sample_spec))
945 : 0 : volume = pa_sw_cvolume_multiply(&v, &v, volume);
946 : : else
947 : 0 : volume = pa_sw_cvolume_multiply_scalar(&v, &v, pa_cvolume_max(volume));
948 : : } else {
949 [ # # ]: 0 : if (!pa_cvolume_compatible(volume, &o->sample_spec)) {
950 : 0 : v = o->volume;
951 : 0 : volume = pa_cvolume_scale(&v, pa_cvolume_max(volume));
952 : : }
953 : : }
954 : :
955 [ # # ]: 0 : if (pa_cvolume_equal(volume, &o->volume)) {
956 [ # # ][ # # ]: 0 : o->save_volume = o->save_volume || save;
957 : 0 : return;
958 : : }
959 : :
960 : 0 : o->volume = *volume;
961 : 0 : o->save_volume = save;
962 : :
963 [ # # ]: 0 : if (pa_source_flat_volume_enabled(o->source)) {
964 : : /* We are in flat volume mode, so let's update all source input
965 : : * volumes and update the flat volume of the source */
966 : :
967 : 0 : pa_source_set_volume(o->source, NULL, TRUE, save);
968 : :
969 : : } else {
970 : : /* OK, we are in normal volume mode. The volume only affects
971 : : * ourselves */
972 : 0 : set_real_ratio(o, volume);
973 : :
974 : : /* Copy the new soft_volume to the thread_info struct */
975 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_SOFT_VOLUME, NULL, 0, NULL) == 0);
976 : : }
977 : :
978 : : /* The volume changed, let's tell people so */
979 [ # # ]: 0 : if (o->volume_changed)
980 : 0 : o->volume_changed(o);
981 : :
982 : : /* The virtual volume changed, let's tell people so */
983 : 0 : pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
984 : : }
985 : :
986 : : /* Called from main context */
987 : 0 : static void set_real_ratio(pa_source_output *o, const pa_cvolume *v) {
988 : 0 : pa_source_output_assert_ref(o);
989 [ # # ]: 0 : pa_assert_ctl_context();
990 [ # # ]: 0 : pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
991 [ # # ][ # # ]: 0 : pa_assert(!v || pa_cvolume_compatible(v, &o->sample_spec));
992 : :
993 : : /* This basically calculates:
994 : : *
995 : : * o->real_ratio := v
996 : : * o->soft_volume := o->real_ratio * o->volume_factor */
997 : :
998 [ # # ]: 0 : if (v)
999 : 0 : o->real_ratio = *v;
1000 : : else
1001 : 0 : pa_cvolume_reset(&o->real_ratio, o->sample_spec.channels);
1002 : :
1003 : 0 : pa_sw_cvolume_multiply(&o->soft_volume, &o->real_ratio, &o->volume_factor);
1004 : : /* We don't copy the data to the thread_info data. That's left for someone else to do */
1005 : 0 : }
1006 : :
1007 : : /* Called from main or I/O context */
1008 : 0 : pa_bool_t pa_source_output_is_passthrough(pa_source_output *o) {
1009 : 0 : pa_source_output_assert_ref(o);
1010 : :
1011 [ # # ]: 0 : if (PA_UNLIKELY(!pa_format_info_is_pcm(o->format)))
1012 : : return TRUE;
1013 : :
1014 [ # # ]: 0 : if (PA_UNLIKELY(o->flags & PA_SOURCE_OUTPUT_PASSTHROUGH))
1015 : : return TRUE;
1016 : :
1017 : 0 : return FALSE;
1018 : : }
1019 : :
1020 : : /* Called from main context */
1021 : 0 : pa_bool_t pa_source_output_is_volume_readable(pa_source_output *o) {
1022 : 0 : pa_source_output_assert_ref(o);
1023 [ # # ]: 0 : pa_assert_ctl_context();
1024 : :
1025 : 0 : return !pa_source_output_is_passthrough(o);
1026 : : }
1027 : :
1028 : : /* Called from main context */
1029 : 0 : pa_cvolume *pa_source_output_get_volume(pa_source_output *o, pa_cvolume *volume, pa_bool_t absolute) {
1030 : 0 : pa_source_output_assert_ref(o);
1031 [ # # ]: 0 : pa_assert_ctl_context();
1032 [ # # ]: 0 : pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
1033 [ # # ]: 0 : pa_assert(pa_source_output_is_volume_readable(o));
1034 : :
1035 [ # # ][ # # ]: 0 : if (absolute || !pa_source_flat_volume_enabled(o->source))
1036 : 0 : *volume = o->volume;
1037 : : else
1038 : 0 : *volume = o->reference_ratio;
1039 : :
1040 : 0 : return volume;
1041 : : }
1042 : :
1043 : : /* Called from main context */
1044 : 0 : void pa_source_output_set_mute(pa_source_output *o, pa_bool_t mute, pa_bool_t save) {
1045 : 0 : pa_source_output_assert_ref(o);
1046 [ # # ]: 0 : pa_assert_ctl_context();
1047 [ # # ]: 0 : pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
1048 : :
1049 [ # # ]: 0 : if (!o->muted == !mute) {
1050 [ # # ][ # # ]: 0 : o->save_muted = o->save_muted || mute;
1051 : 0 : return;
1052 : : }
1053 : :
1054 : 0 : o->muted = mute;
1055 : 0 : o->save_muted = save;
1056 : :
1057 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_SOFT_MUTE, NULL, 0, NULL) == 0);
1058 : :
1059 : : /* The mute status changed, let's tell people so */
1060 [ # # ]: 0 : if (o->mute_changed)
1061 : 0 : o->mute_changed(o);
1062 : :
1063 : 0 : pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
1064 : : }
1065 : :
1066 : : /* Called from main context */
1067 : 0 : pa_bool_t pa_source_output_get_mute(pa_source_output *o) {
1068 : 0 : pa_source_output_assert_ref(o);
1069 [ # # ]: 0 : pa_assert_ctl_context();
1070 [ # # ]: 0 : pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
1071 : :
1072 : 0 : return o->muted;
1073 : : }
1074 : :
1075 : : /* Called from main thread */
1076 : 0 : void pa_source_output_update_proplist(pa_source_output *o, pa_update_mode_t mode, pa_proplist *p) {
1077 : 0 : pa_source_output_assert_ref(o);
1078 [ # # ]: 0 : pa_assert_ctl_context();
1079 : :
1080 [ # # ]: 0 : if (p)
1081 : 0 : pa_proplist_update(o->proplist, mode, p);
1082 : :
1083 [ # # ]: 0 : if (PA_SOURCE_OUTPUT_IS_LINKED(o->state)) {
1084 : 0 : pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED], o);
1085 : 0 : pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
1086 : : }
1087 : 0 : }
1088 : :
1089 : : /* Called from main context */
1090 : 0 : void pa_source_output_cork(pa_source_output *o, pa_bool_t b) {
1091 : 0 : pa_source_output_assert_ref(o);
1092 [ # # ]: 0 : pa_assert_ctl_context();
1093 [ # # ]: 0 : pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
1094 : :
1095 [ # # ]: 0 : source_output_set_state(o, b ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING);
1096 : 0 : }
1097 : :
1098 : : /* Called from main context */
1099 : 0 : int pa_source_output_set_rate(pa_source_output *o, uint32_t rate) {
1100 : 0 : pa_source_output_assert_ref(o);
1101 [ # # ]: 0 : pa_assert_ctl_context();
1102 [ # # ]: 0 : pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
1103 [ # # ]: 0 : pa_return_val_if_fail(o->thread_info.resampler, -PA_ERR_BADSTATE);
1104 : :
1105 [ # # ]: 0 : if (o->sample_spec.rate == rate)
1106 : : return 0;
1107 : :
1108 : 0 : o->sample_spec.rate = rate;
1109 : :
1110 : 0 : pa_asyncmsgq_post(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_RATE, PA_UINT_TO_PTR(rate), 0, NULL, NULL);
1111 : :
1112 : 0 : pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
1113 : 0 : return 0;
1114 : : }
1115 : :
1116 : : /* Called from main context */
1117 : 0 : void pa_source_output_set_name(pa_source_output *o, const char *name) {
1118 : : const char *old;
1119 [ # # ]: 0 : pa_assert_ctl_context();
1120 : 0 : pa_source_output_assert_ref(o);
1121 : :
1122 [ # # ][ # # ]: 0 : if (!name && !pa_proplist_contains(o->proplist, PA_PROP_MEDIA_NAME))
1123 : : return;
1124 : :
1125 : 0 : old = pa_proplist_gets(o->proplist, PA_PROP_MEDIA_NAME);
1126 : :
1127 [ # # ][ # # ]: 0 : if (old && name && pa_streq(old, name))
1128 : : return;
1129 : :
1130 [ # # ]: 0 : if (name)
1131 : 0 : pa_proplist_sets(o->proplist, PA_PROP_MEDIA_NAME, name);
1132 : : else
1133 : 0 : pa_proplist_unset(o->proplist, PA_PROP_MEDIA_NAME);
1134 : :
1135 [ # # ]: 0 : if (PA_SOURCE_OUTPUT_IS_LINKED(o->state)) {
1136 : 0 : pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED], o);
1137 : 0 : pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
1138 : : }
1139 : : }
1140 : :
1141 : : /* Called from main context */
1142 : 0 : pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o) {
1143 : 0 : pa_source_output_assert_ref(o);
1144 [ # # ]: 0 : pa_assert_ctl_context();
1145 : :
1146 : 0 : return o->actual_resample_method;
1147 : : }
1148 : :
1149 : : /* Called from main context */
1150 : 0 : pa_bool_t pa_source_output_may_move(pa_source_output *o) {
1151 : 0 : pa_source_output_assert_ref(o);
1152 [ # # ]: 0 : pa_assert_ctl_context();
1153 [ # # ]: 0 : pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
1154 : :
1155 [ # # ]: 0 : if (o->flags & PA_SOURCE_OUTPUT_DONT_MOVE)
1156 : : return FALSE;
1157 : :
1158 [ # # ]: 0 : if (o->direct_on_input)
1159 : : return FALSE;
1160 : :
1161 : 0 : return TRUE;
1162 : : }
1163 : :
1164 : : static pa_bool_t find_filter_source_output(pa_source_output *target, pa_source *s) {
1165 : : int i = 0;
1166 [ # # ][ # # ]: 0 : while (s && s->output_from_master) {
1167 [ # # ]: 0 : if (s->output_from_master == target)
1168 : : return TRUE;
1169 : 0 : s = s->output_from_master->source;
1170 [ # # ]: 0 : pa_assert(i++ < 100);
1171 : : }
1172 : : return FALSE;
1173 : : }
1174 : :
1175 : : /* Called from main context */
1176 : 0 : pa_bool_t pa_source_output_may_move_to(pa_source_output *o, pa_source *dest) {
1177 : 0 : pa_source_output_assert_ref(o);
1178 [ # # ]: 0 : pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
1179 : 0 : pa_source_assert_ref(dest);
1180 : :
1181 [ # # ]: 0 : if (dest == o->source)
1182 : : return TRUE;
1183 : :
1184 [ # # ]: 0 : if (!pa_source_output_may_move(o))
1185 : : return FALSE;
1186 : :
1187 : : /* Make sure we're not creating a filter source cycle */
1188 [ # # ]: 0 : if (find_filter_source_output(o, dest)) {
1189 : 0 : pa_log_debug("Can't connect output to %s, as that would create a cycle.", dest->name);
1190 : 0 : return FALSE;
1191 : : }
1192 : :
1193 [ # # ]: 0 : if (pa_idxset_size(dest->outputs) >= PA_MAX_OUTPUTS_PER_SOURCE) {
1194 : 0 : pa_log_warn("Failed to move source output: too many outputs per source.");
1195 : 0 : return FALSE;
1196 : : }
1197 : :
1198 [ # # ]: 0 : if (o->may_move_to)
1199 [ # # ]: 0 : if (!o->may_move_to(o, dest))
1200 : : return FALSE;
1201 : :
1202 : 0 : return TRUE;
1203 : : }
1204 : :
1205 : : /* Called from main context */
1206 : 0 : int pa_source_output_start_move(pa_source_output *o) {
1207 : : pa_source *origin;
1208 : : int r;
1209 : :
1210 : 0 : pa_source_output_assert_ref(o);
1211 [ # # ]: 0 : pa_assert_ctl_context();
1212 [ # # ]: 0 : pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
1213 [ # # ]: 0 : pa_assert(o->source);
1214 : :
1215 [ # # ]: 0 : if (!pa_source_output_may_move(o))
1216 : : return -PA_ERR_NOTSUPPORTED;
1217 : :
1218 [ # # ]: 0 : if ((r = pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_START], o)) < 0)
1219 : : return r;
1220 : :
1221 : 0 : origin = o->source;
1222 : :
1223 : 0 : pa_idxset_remove_by_data(o->source->outputs, o, NULL);
1224 : :
1225 [ # # ]: 0 : if (pa_source_output_get_state(o) == PA_SOURCE_OUTPUT_CORKED)
1226 [ # # ]: 0 : pa_assert_se(origin->n_corked-- >= 1);
1227 : :
1228 [ # # ]: 0 : if (pa_source_output_is_passthrough(o))
1229 : 0 : pa_source_leave_passthrough(o->source);
1230 : :
1231 [ # # ]: 0 : if (pa_source_flat_volume_enabled(o->source))
1232 : : /* We might need to update the source's volume if we are in flat
1233 : : * volume mode. */
1234 : 0 : pa_source_set_volume(o->source, NULL, FALSE, FALSE);
1235 : :
1236 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL) == 0);
1237 : :
1238 : 0 : pa_source_update_status(o->source);
1239 : 0 : o->source = NULL;
1240 : :
1241 : 0 : pa_source_output_unref(o);
1242 : :
1243 : 0 : return 0;
1244 : : }
1245 : :
1246 : : /* Called from main context. If it has an origin source that uses volume sharing,
1247 : : * then also the origin source and all streams connected to it need to update
1248 : : * their volume - this function does all that by using recursion. */
1249 : 0 : static void update_volume_due_to_moving(pa_source_output *o, pa_source *dest) {
1250 : : pa_cvolume old_volume;
1251 : :
1252 [ # # ]: 0 : pa_assert(o);
1253 [ # # ]: 0 : pa_assert(dest);
1254 [ # # ]: 0 : pa_assert(o->source); /* The destination source should already be set. */
1255 : :
1256 [ # # ][ # # ]: 0 : if (o->destination_source && (o->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)) {
1257 : : pa_source *root_source;
1258 : : pa_source_output *destination_source_output;
1259 : : uint32_t idx;
1260 : :
1261 : 0 : root_source = pa_source_get_master(o->source);
1262 : :
1263 [ # # ]: 0 : if (PA_UNLIKELY(!root_source))
1264 : 0 : return;
1265 : :
1266 [ # # ]: 0 : if (pa_source_flat_volume_enabled(o->source)) {
1267 : : /* Ok, so the origin source uses volume sharing, and flat volume is
1268 : : * enabled. The volume will have to be updated as follows:
1269 : : *
1270 : : * o->volume := o->source->real_volume
1271 : : * (handled later by pa_source_set_volume)
1272 : : * o->reference_ratio := o->volume / o->source->reference_volume
1273 : : * (handled later by pa_source_set_volume)
1274 : : * o->real_ratio stays unchanged
1275 : : * (streams whose origin source uses volume sharing should
1276 : : * always have real_ratio of 0 dB)
1277 : : * o->soft_volume stays unchanged
1278 : : * (streams whose origin source uses volume sharing should
1279 : : * always have volume_factor as soft_volume, so no change
1280 : : * should be needed) */
1281 : :
1282 [ # # ]: 0 : pa_assert(pa_cvolume_is_norm(&o->real_ratio));
1283 [ # # ]: 0 : pa_assert(pa_cvolume_equal(&o->soft_volume, &o->volume_factor));
1284 : :
1285 : : /* Notifications will be sent by pa_source_set_volume(). */
1286 : :
1287 : : } else {
1288 : : /* Ok, so the origin source uses volume sharing, and flat volume is
1289 : : * disabled. The volume will have to be updated as follows:
1290 : : *
1291 : : * o->volume := 0 dB
1292 : : * o->reference_ratio := 0 dB
1293 : : * o->real_ratio stays unchanged
1294 : : * (streams whose origin source uses volume sharing should
1295 : : * always have real_ratio of 0 dB)
1296 : : * o->soft_volume stays unchanged
1297 : : * (streams whose origin source uses volume sharing should
1298 : : * always have volume_factor as soft_volume, so no change
1299 : : * should be needed) */
1300 : :
1301 : 0 : old_volume = o->volume;
1302 : 0 : pa_cvolume_reset(&o->volume, o->volume.channels);
1303 : 0 : pa_cvolume_reset(&o->reference_ratio, o->reference_ratio.channels);
1304 [ # # ]: 0 : pa_assert(pa_cvolume_is_norm(&o->real_ratio));
1305 [ # # ]: 0 : pa_assert(pa_cvolume_equal(&o->soft_volume, &o->volume_factor));
1306 : :
1307 : : /* Notify others about the changed source output volume. */
1308 [ # # ]: 0 : if (!pa_cvolume_equal(&o->volume, &old_volume)) {
1309 [ # # ]: 0 : if (o->volume_changed)
1310 : 0 : o->volume_changed(o);
1311 : :
1312 : 0 : pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
1313 : : }
1314 : : }
1315 : :
1316 : : /* Additionally, the origin source volume needs updating:
1317 : : *
1318 : : * o->destination_source->reference_volume := root_source->reference_volume
1319 : : * o->destination_source->real_volume := root_source->real_volume
1320 : : * o->destination_source->soft_volume stays unchanged
1321 : : * (sources that use volume sharing should always have
1322 : : * soft_volume of 0 dB) */
1323 : :
1324 : 0 : old_volume = o->destination_source->reference_volume;
1325 : :
1326 : 0 : o->destination_source->reference_volume = root_source->reference_volume;
1327 : 0 : pa_cvolume_remap(&o->destination_source->reference_volume, &root_source->channel_map, &o->destination_source->channel_map);
1328 : :
1329 : 0 : o->destination_source->real_volume = root_source->real_volume;
1330 : 0 : pa_cvolume_remap(&o->destination_source->real_volume, &root_source->channel_map, &o->destination_source->channel_map);
1331 : :
1332 [ # # ]: 0 : pa_assert(pa_cvolume_is_norm(&o->destination_source->soft_volume));
1333 : :
1334 : : /* Notify others about the changed source volume. If you wonder whether
1335 : : * o->destination_source->set_volume() should be called somewhere, that's not
1336 : : * the case, because sources that use volume sharing shouldn't have any
1337 : : * internal volume that set_volume() would update. If you wonder
1338 : : * whether the thread_info variables should be synced, yes, they
1339 : : * should, and it's done by the PA_SOURCE_MESSAGE_FINISH_MOVE message
1340 : : * handler. */
1341 [ # # ]: 0 : if (!pa_cvolume_equal(&o->destination_source->reference_volume, &old_volume))
1342 : 0 : pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, o->destination_source->index);
1343 : :
1344 : : /* Recursively update origin source outputs. */
1345 [ # # ]: 0 : PA_IDXSET_FOREACH(destination_source_output, o->destination_source->outputs, idx)
1346 : 0 : update_volume_due_to_moving(destination_source_output, dest);
1347 : :
1348 : : } else {
1349 : 0 : old_volume = o->volume;
1350 : :
1351 [ # # ]: 0 : if (pa_source_flat_volume_enabled(o->source)) {
1352 : : /* Ok, so this is a regular stream, and flat volume is enabled. The
1353 : : * volume will have to be updated as follows:
1354 : : *
1355 : : * o->volume := o->reference_ratio * o->source->reference_volume
1356 : : * o->reference_ratio stays unchanged
1357 : : * o->real_ratio := o->volume / o->source->real_volume
1358 : : * (handled later by pa_source_set_volume)
1359 : : * o->soft_volume := o->real_ratio * o->volume_factor
1360 : : * (handled later by pa_source_set_volume) */
1361 : :
1362 : 0 : o->volume = o->source->reference_volume;
1363 : 0 : pa_cvolume_remap(&o->volume, &o->source->channel_map, &o->channel_map);
1364 : 0 : pa_sw_cvolume_multiply(&o->volume, &o->volume, &o->reference_ratio);
1365 : :
1366 : : } else {
1367 : : /* Ok, so this is a regular stream, and flat volume is disabled.
1368 : : * The volume will have to be updated as follows:
1369 : : *
1370 : : * o->volume := o->reference_ratio
1371 : : * o->reference_ratio stays unchanged
1372 : : * o->real_ratio := o->reference_ratio
1373 : : * o->soft_volume := o->real_ratio * o->volume_factor */
1374 : :
1375 : 0 : o->volume = o->reference_ratio;
1376 : 0 : o->real_ratio = o->reference_ratio;
1377 : 0 : pa_sw_cvolume_multiply(&o->soft_volume, &o->real_ratio, &o->volume_factor);
1378 : : }
1379 : :
1380 : : /* Notify others about the changed source output volume. */
1381 [ # # ]: 0 : if (!pa_cvolume_equal(&o->volume, &old_volume)) {
1382 : : /* XXX: In case o->source has flat volume enabled, then real_ratio
1383 : : * and soft_volume are not updated yet. Let's hope that the
1384 : : * callback implementation doesn't care about those variables... */
1385 [ # # ]: 0 : if (o->volume_changed)
1386 : 0 : o->volume_changed(o);
1387 : :
1388 : 0 : pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
1389 : : }
1390 : : }
1391 : :
1392 : : /* If o->source == dest, then recursion has finished, and we can finally call
1393 : : * pa_source_set_volume(), which will do the rest of the updates. */
1394 [ # # ][ # # ]: 0 : if ((o->source == dest) && pa_source_flat_volume_enabled(o->source))
1395 : 0 : pa_source_set_volume(o->source, NULL, FALSE, o->save_volume);
1396 : : }
1397 : :
1398 : : /* Called from main context */
1399 : 0 : int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, pa_bool_t save) {
1400 : 0 : pa_source_output_assert_ref(o);
1401 [ # # ]: 0 : pa_assert_ctl_context();
1402 [ # # ]: 0 : pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
1403 [ # # ]: 0 : pa_assert(!o->source);
1404 : 0 : pa_source_assert_ref(dest);
1405 : :
1406 [ # # ]: 0 : if (!pa_source_output_may_move_to(o, dest))
1407 : : return -PA_ERR_NOTSUPPORTED;
1408 : :
1409 [ # # ][ # # ]: 0 : if (pa_source_output_is_passthrough(o) && !pa_source_check_format(dest, o->format)) {
1410 : 0 : pa_proplist *p = pa_proplist_new();
1411 : 0 : pa_log_debug("New source doesn't support stream format, sending format-changed and killing");
1412 : : /* Tell the client what device we want to be on if it is going to
1413 : : * reconnect */
1414 : 0 : pa_proplist_sets(p, "device", dest->name);
1415 : 0 : pa_source_output_send_event(o, PA_STREAM_EVENT_FORMAT_LOST, p);
1416 : 0 : pa_proplist_free(p);
1417 : 0 : return -PA_ERR_NOTSUPPORTED;
1418 : : }
1419 : :
1420 [ # # # # ]: 0 : if (!(o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) &&
1421 : 0 : !pa_sample_spec_equal(&o->sample_spec, &dest->sample_spec)){
1422 : : /* try to change dest sink rate if possible without glitches.
1423 : : module-suspend-on-idle resumes destination source with
1424 : : SOURCE_OUTPUT_MOVE_FINISH hook */
1425 : :
1426 : 0 : pa_log_info("Trying to change sample rate");
1427 [ # # ]: 0 : if (pa_source_update_rate(dest, o->sample_spec.rate, pa_source_output_is_passthrough(o)) == TRUE)
1428 : 0 : pa_log_info("Rate changed to %u Hz",
1429 : : dest->sample_spec.rate);
1430 : : else
1431 : 0 : pa_log_info("Resampling enabled to %u Hz",
1432 : : dest->sample_spec.rate);
1433 : : }
1434 : :
1435 [ # # ]: 0 : if (o->moving)
1436 : 0 : o->moving(o, dest);
1437 : :
1438 : 0 : o->source = dest;
1439 : 0 : o->save_source = save;
1440 : 0 : pa_idxset_put(o->source->outputs, pa_source_output_ref(o), NULL);
1441 : :
1442 : 0 : pa_cvolume_remap(&o->volume_factor_source, &o->channel_map, &o->source->channel_map);
1443 : :
1444 [ # # ]: 0 : if (pa_source_output_get_state(o) == PA_SOURCE_OUTPUT_CORKED)
1445 : 0 : o->source->n_corked++;
1446 : :
1447 : 0 : pa_source_output_update_rate(o);
1448 : :
1449 : 0 : pa_source_update_status(dest);
1450 : :
1451 : 0 : update_volume_due_to_moving(o, dest);
1452 : :
1453 [ # # ]: 0 : if (pa_source_output_is_passthrough(o))
1454 : 0 : pa_source_enter_passthrough(o->source);
1455 : :
1456 [ # # ]: 0 : pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_ADD_OUTPUT, o, 0, NULL) == 0);
1457 : :
1458 : 0 : pa_log_debug("Successfully moved source output %i to %s.", o->index, dest->name);
1459 : :
1460 : : /* Notify everyone */
1461 : 0 : pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FINISH], o);
1462 : 0 : pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
1463 : :
1464 : 0 : return 0;
1465 : : }
1466 : :
1467 : : /* Called from main context */
1468 : 0 : void pa_source_output_fail_move(pa_source_output *o) {
1469 : :
1470 : 0 : pa_source_output_assert_ref(o);
1471 [ # # ]: 0 : pa_assert_ctl_context();
1472 [ # # ]: 0 : pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
1473 [ # # ]: 0 : pa_assert(!o->source);
1474 : :
1475 : : /* Check if someone wants this source output? */
1476 [ # # ]: 0 : if (pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FAIL], o) == PA_HOOK_STOP)
1477 : 0 : return;
1478 : :
1479 [ # # ]: 0 : if (o->moving)
1480 : 0 : o->moving(o, NULL);
1481 : :
1482 : 0 : pa_source_output_kill(o);
1483 : : }
1484 : :
1485 : : /* Called from main context */
1486 : 0 : int pa_source_output_move_to(pa_source_output *o, pa_source *dest, pa_bool_t save) {
1487 : : int r;
1488 : :
1489 : 0 : pa_source_output_assert_ref(o);
1490 [ # # ]: 0 : pa_assert_ctl_context();
1491 [ # # ]: 0 : pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
1492 [ # # ]: 0 : pa_assert(o->source);
1493 : 0 : pa_source_assert_ref(dest);
1494 : :
1495 [ # # ]: 0 : if (dest == o->source)
1496 : : return 0;
1497 : :
1498 [ # # ]: 0 : if (!pa_source_output_may_move_to(o, dest))
1499 : : return -PA_ERR_NOTSUPPORTED;
1500 : :
1501 : 0 : pa_source_output_ref(o);
1502 : :
1503 [ # # ]: 0 : if ((r = pa_source_output_start_move(o)) < 0) {
1504 : 0 : pa_source_output_unref(o);
1505 : 0 : return r;
1506 : : }
1507 : :
1508 [ # # ]: 0 : if ((r = pa_source_output_finish_move(o, dest, save)) < 0) {
1509 : 0 : pa_source_output_fail_move(o);
1510 : 0 : pa_source_output_unref(o);
1511 : 0 : return r;
1512 : : }
1513 : :
1514 : 0 : pa_source_output_unref(o);
1515 : :
1516 : 0 : return 0;
1517 : : }
1518 : :
1519 : : /* Called from IO thread context */
1520 : 0 : void pa_source_output_set_state_within_thread(pa_source_output *o, pa_source_output_state_t state) {
1521 : 0 : pa_source_output_assert_ref(o);
1522 [ # # ][ # # ]: 0 : pa_source_output_assert_io_context(o);
1523 : :
1524 [ # # ]: 0 : if (state == o->thread_info.state)
1525 : 0 : return;
1526 : :
1527 [ # # ]: 0 : if (o->state_change)
1528 : 0 : o->state_change(o, state);
1529 : :
1530 : 0 : o->thread_info.state = state;
1531 : : }
1532 : :
1533 : : /* Called from IO thread context, except when it is not */
1534 : 0 : int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int64_t offset, pa_memchunk* chunk) {
1535 : 0 : pa_source_output *o = PA_SOURCE_OUTPUT(mo);
1536 : 0 : pa_source_output_assert_ref(o);
1537 : :
1538 [ # # # # : 0 : switch (code) {
# # # # ]
1539 : :
1540 : : case PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY: {
1541 : 0 : pa_usec_t *r = userdata;
1542 : :
1543 : 0 : r[0] += pa_bytes_to_usec(pa_memblockq_get_length(o->thread_info.delay_memblockq), &o->source->sample_spec);
1544 : 0 : r[1] += pa_source_get_latency_within_thread(o->source);
1545 : :
1546 : 0 : return 0;
1547 : : }
1548 : :
1549 : : case PA_SOURCE_OUTPUT_MESSAGE_SET_RATE:
1550 : :
1551 : 0 : o->thread_info.sample_spec.rate = PA_PTR_TO_UINT(userdata);
1552 : 0 : pa_resampler_set_output_rate(o->thread_info.resampler, PA_PTR_TO_UINT(userdata));
1553 : 0 : return 0;
1554 : :
1555 : : case PA_SOURCE_OUTPUT_MESSAGE_SET_STATE:
1556 : :
1557 : 0 : pa_source_output_set_state_within_thread(o, PA_PTR_TO_UINT(userdata));
1558 : :
1559 : 0 : return 0;
1560 : :
1561 : : case PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY: {
1562 : 0 : pa_usec_t *usec = userdata;
1563 : :
1564 : 0 : *usec = pa_source_output_set_requested_latency_within_thread(o, *usec);
1565 : :
1566 : 0 : return 0;
1567 : : }
1568 : :
1569 : : case PA_SOURCE_OUTPUT_MESSAGE_GET_REQUESTED_LATENCY: {
1570 : 0 : pa_usec_t *r = userdata;
1571 : :
1572 : 0 : *r = o->thread_info.requested_source_latency;
1573 : 0 : return 0;
1574 : : }
1575 : :
1576 : : case PA_SOURCE_OUTPUT_MESSAGE_SET_SOFT_VOLUME:
1577 [ # # ]: 0 : if (!pa_cvolume_equal(&o->thread_info.soft_volume, &o->soft_volume)) {
1578 : 0 : o->thread_info.soft_volume = o->soft_volume;
1579 : : }
1580 : : return 0;
1581 : :
1582 : : case PA_SOURCE_OUTPUT_MESSAGE_SET_SOFT_MUTE:
1583 [ # # ]: 0 : if (o->thread_info.muted != o->muted) {
1584 : 0 : o->thread_info.muted = o->muted;
1585 : : }
1586 : : return 0;
1587 : : }
1588 : :
1589 : : return -PA_ERR_NOTIMPLEMENTED;
1590 : : }
1591 : :
1592 : : /* Called from main context */
1593 : 0 : void pa_source_output_send_event(pa_source_output *o, const char *event, pa_proplist *data) {
1594 : 0 : pa_proplist *pl = NULL;
1595 : : pa_source_output_send_event_hook_data hook_data;
1596 : :
1597 : 0 : pa_source_output_assert_ref(o);
1598 [ # # ]: 0 : pa_assert_ctl_context();
1599 [ # # ]: 0 : pa_assert(event);
1600 : :
1601 [ # # ]: 0 : if (!o->send_event)
1602 : 0 : return;
1603 : :
1604 [ # # ]: 0 : if (!data)
1605 : 0 : data = pl = pa_proplist_new();
1606 : :
1607 : 0 : hook_data.source_output = o;
1608 : 0 : hook_data.data = data;
1609 : 0 : hook_data.event = event;
1610 : :
1611 [ # # ]: 0 : if (pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_SEND_EVENT], &hook_data) < 0)
1612 : : goto finish;
1613 : :
1614 : 0 : o->send_event(o, event, data);
1615 : :
1616 : : finish:
1617 [ # # ]: 0 : if (pl)
1618 : 0 : pa_proplist_free(pl);
1619 : : }
1620 : :
1621 : : /* Called from main context */
1622 : : /* Updates the source output's resampler with whatever the current source
1623 : : * requires -- useful when the underlying source's rate might have changed */
1624 : 0 : int pa_source_output_update_rate(pa_source_output *o) {
1625 : : pa_resampler *new_resampler;
1626 : : char *memblockq_name;
1627 : :
1628 : 0 : pa_source_output_assert_ref(o);
1629 [ # # ]: 0 : pa_assert_ctl_context();
1630 : :
1631 [ # # # # ]: 0 : if (o->thread_info.resampler &&
1632 [ # # ]: 0 : pa_sample_spec_equal(pa_resampler_input_sample_spec(o->thread_info.resampler), &o->source->sample_spec) &&
1633 : 0 : pa_channel_map_equal(pa_resampler_input_channel_map(o->thread_info.resampler), &o->source->channel_map))
1634 : :
1635 : 0 : new_resampler = o->thread_info.resampler;
1636 : :
1637 [ # # ][ # # ]: 0 : else if (!pa_source_output_is_passthrough(o) &&
1638 [ # # ]: 0 : ((o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ||
1639 [ # # ]: 0 : !pa_sample_spec_equal(&o->sample_spec, &o->source->sample_spec) ||
1640 : 0 : !pa_channel_map_equal(&o->channel_map, &o->source->channel_map))) {
1641 : :
1642 : 0 : new_resampler = pa_resampler_new(o->core->mempool,
1643 : 0 : &o->source->sample_spec, &o->source->channel_map,
1644 : 0 : &o->sample_spec, &o->channel_map,
1645 : : o->requested_resample_method,
1646 : 0 : ((o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
1647 [ # # ]: 0 : ((o->flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
1648 [ # # ][ # # ]: 0 : (o->core->disable_remixing || (o->flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0));
1649 : :
1650 [ # # ]: 0 : if (!new_resampler) {
1651 : 0 : pa_log_warn("Unsupported resampling operation.");
1652 : 0 : return -PA_ERR_NOTSUPPORTED;
1653 : : }
1654 : : } else
1655 : : new_resampler = NULL;
1656 : :
1657 [ # # ]: 0 : if (new_resampler == o->thread_info.resampler)
1658 : : return 0;
1659 : :
1660 [ # # ]: 0 : if (o->thread_info.resampler)
1661 : 0 : pa_resampler_free(o->thread_info.resampler);
1662 : :
1663 : 0 : o->thread_info.resampler = new_resampler;
1664 : :
1665 : 0 : pa_memblockq_free(o->thread_info.delay_memblockq);
1666 : :
1667 : 0 : memblockq_name = pa_sprintf_malloc("source output delay_memblockq [%u]", o->index);
1668 : 0 : o->thread_info.delay_memblockq = pa_memblockq_new(
1669 : : memblockq_name,
1670 : : 0,
1671 : : MEMBLOCKQ_MAXLENGTH,
1672 : : 0,
1673 : 0 : &o->source->sample_spec,
1674 : : 0,
1675 : : 1,
1676 : : 0,
1677 : 0 : &o->source->silence);
1678 : 0 : pa_xfree(memblockq_name);
1679 : :
1680 [ # # ]: 0 : o->actual_resample_method = new_resampler ? pa_resampler_get_method(new_resampler) : PA_RESAMPLER_INVALID;
1681 : :
1682 : 0 : pa_log_debug("Updated resampler for source output %d", o->index);
1683 : :
1684 : 0 : return 0;
1685 : : }
|