mirror of
				https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
				synced 2025-11-03 09:01:50 -05:00 
			
		
		
		
	replace sink/source SET_STATE handlers with callbacks
There are no behaviour changes, the code from almost all the SET_STATE handlers is moved with minimal changes to the newly introduced set_state_in_io_thread() callback. The only exception is module-tunnel, which has to call pa_sink_render() after pa_sink.thread_info.state has been updated. The set_state_in_io_thread() callback is called before updating that variable, so moving the SET_STATE handler code to the callback isn't possible. The purpose of this change is to make it easier to get state change handling right in modules. Hooking to the SET_STATE messages in modules required care in calling pa_sink/source_process_msg() at the right time (or not calling it at all, as was the case on resume failures), and there were a few bugs (fixed before this patch). Now the core takes care of ordering things correctly. Another motivation for this change is that there was some talk about adding a suspend_cause variable to pa_sink/source.thread_info. The variable would be updated in the core SET_STATE handler, but that would not work with the old design, because in case of resume failures modules didn't call the core message handler.
This commit is contained in:
		
							parent
							
								
									73b8a57078
								
							
						
					
					
						commit
						b2537a8f38
					
				
					 25 changed files with 874 additions and 658 deletions
				
			
		| 
						 | 
					@ -1184,46 +1184,6 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return 0;
 | 
					            return 0;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					 | 
				
			||||||
        case PA_SINK_MESSAGE_SET_STATE:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                case PA_SINK_SUSPENDED: {
 | 
					 | 
				
			||||||
                    pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    suspend(u);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    break;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                case PA_SINK_IDLE:
 | 
					 | 
				
			||||||
                case PA_SINK_RUNNING: {
 | 
					 | 
				
			||||||
                    int r;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    if (u->sink->thread_info.state == PA_SINK_INIT) {
 | 
					 | 
				
			||||||
                        if (build_pollfd(u) < 0)
 | 
					 | 
				
			||||||
                            /* FIXME: This will cause an assertion failure in
 | 
					 | 
				
			||||||
                             * pa_sink_put(), because with the current design
 | 
					 | 
				
			||||||
                             * pa_sink_put() is not allowed to fail. */
 | 
					 | 
				
			||||||
                            return -PA_ERR_IO;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    if (u->sink->thread_info.state == PA_SINK_SUSPENDED) {
 | 
					 | 
				
			||||||
                        if ((r = unsuspend(u)) < 0)
 | 
					 | 
				
			||||||
                            return r;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    break;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                case PA_SINK_UNLINKED:
 | 
					 | 
				
			||||||
                case PA_SINK_INIT:
 | 
					 | 
				
			||||||
                case PA_SINK_INVALID_STATE:
 | 
					 | 
				
			||||||
                    ;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return pa_sink_process_msg(o, code, data, offset, chunk);
 | 
					    return pa_sink_process_msg(o, code, data, offset, chunk);
 | 
				
			||||||
| 
						 | 
					@ -1248,6 +1208,54 @@ static int sink_set_state_in_main_thread_cb(pa_sink *s, pa_sink_state_t new_stat
 | 
				
			||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Called from the IO thread. */
 | 
				
			||||||
 | 
					static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state) {
 | 
				
			||||||
 | 
					    struct userdata *u;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pa_assert(s);
 | 
				
			||||||
 | 
					    pa_assert_se(u = s->userdata);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    switch (new_state) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case PA_SINK_SUSPENDED: {
 | 
				
			||||||
 | 
					            pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            suspend(u);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case PA_SINK_IDLE:
 | 
				
			||||||
 | 
					        case PA_SINK_RUNNING: {
 | 
				
			||||||
 | 
					            int r;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (u->sink->thread_info.state == PA_SINK_INIT) {
 | 
				
			||||||
 | 
					                if (build_pollfd(u) < 0)
 | 
				
			||||||
 | 
					                    /* FIXME: This will cause an assertion failure, because
 | 
				
			||||||
 | 
					                     * with the current design pa_sink_put() is not allowed
 | 
				
			||||||
 | 
					                     * to fail and pa_sink_put() has no fallback code that
 | 
				
			||||||
 | 
					                     * would start the sink suspended if opening the device
 | 
				
			||||||
 | 
					                     * fails. */
 | 
				
			||||||
 | 
					                    return -PA_ERR_IO;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (u->sink->thread_info.state == PA_SINK_SUSPENDED) {
 | 
				
			||||||
 | 
					                if ((r = unsuspend(u)) < 0)
 | 
				
			||||||
 | 
					                    return r;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case PA_SINK_UNLINKED:
 | 
				
			||||||
 | 
					        case PA_SINK_INIT:
 | 
				
			||||||
 | 
					        case PA_SINK_INVALID_STATE:
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int ctl_mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
 | 
					static int ctl_mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
 | 
				
			||||||
    struct userdata *u = snd_mixer_elem_get_callback_private(elem);
 | 
					    struct userdata *u = snd_mixer_elem_get_callback_private(elem);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2360,6 +2368,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
 | 
				
			||||||
    if (u->use_tsched)
 | 
					    if (u->use_tsched)
 | 
				
			||||||
        u->sink->update_requested_latency = sink_update_requested_latency_cb;
 | 
					        u->sink->update_requested_latency = sink_update_requested_latency_cb;
 | 
				
			||||||
    u->sink->set_state_in_main_thread = sink_set_state_in_main_thread_cb;
 | 
					    u->sink->set_state_in_main_thread = sink_set_state_in_main_thread_cb;
 | 
				
			||||||
 | 
					    u->sink->set_state_in_io_thread = sink_set_state_in_io_thread_cb;
 | 
				
			||||||
    if (u->ucm_context)
 | 
					    if (u->ucm_context)
 | 
				
			||||||
        u->sink->set_port = sink_set_port_ucm_cb;
 | 
					        u->sink->set_port = sink_set_port_ucm_cb;
 | 
				
			||||||
    else
 | 
					    else
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1039,46 +1039,6 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return 0;
 | 
					            return 0;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					 | 
				
			||||||
        case PA_SOURCE_MESSAGE_SET_STATE:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                case PA_SOURCE_SUSPENDED: {
 | 
					 | 
				
			||||||
                    pa_assert(PA_SOURCE_IS_OPENED(u->source->thread_info.state));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    suspend(u);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    break;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                case PA_SOURCE_IDLE:
 | 
					 | 
				
			||||||
                case PA_SOURCE_RUNNING: {
 | 
					 | 
				
			||||||
                    int r;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    if (u->source->thread_info.state == PA_SOURCE_INIT) {
 | 
					 | 
				
			||||||
                        if (build_pollfd(u) < 0)
 | 
					 | 
				
			||||||
                            /* FIXME: This will cause an assertion failure in
 | 
					 | 
				
			||||||
                             * pa_source_put(), because with the current design
 | 
					 | 
				
			||||||
                             * pa_source_put() is not allowed to fail. */
 | 
					 | 
				
			||||||
                            return -PA_ERR_IO;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    if (u->source->thread_info.state == PA_SOURCE_SUSPENDED) {
 | 
					 | 
				
			||||||
                        if ((r = unsuspend(u)) < 0)
 | 
					 | 
				
			||||||
                            return r;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    break;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                case PA_SOURCE_UNLINKED:
 | 
					 | 
				
			||||||
                case PA_SOURCE_INIT:
 | 
					 | 
				
			||||||
                case PA_SOURCE_INVALID_STATE:
 | 
					 | 
				
			||||||
                    ;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return pa_source_process_msg(o, code, data, offset, chunk);
 | 
					    return pa_source_process_msg(o, code, data, offset, chunk);
 | 
				
			||||||
| 
						 | 
					@ -1103,6 +1063,54 @@ static int source_set_state_in_main_thread_cb(pa_source *s, pa_source_state_t ne
 | 
				
			||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Called from the IO thread. */
 | 
				
			||||||
 | 
					static int source_set_state_in_io_thread_cb(pa_source *s, pa_source_state_t new_state) {
 | 
				
			||||||
 | 
					    struct userdata *u;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pa_assert(s);
 | 
				
			||||||
 | 
					    pa_assert_se(u = s->userdata);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    switch (new_state) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case PA_SOURCE_SUSPENDED: {
 | 
				
			||||||
 | 
					            pa_assert(PA_SOURCE_IS_OPENED(u->source->thread_info.state));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            suspend(u);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case PA_SOURCE_IDLE:
 | 
				
			||||||
 | 
					        case PA_SOURCE_RUNNING: {
 | 
				
			||||||
 | 
					            int r;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (u->source->thread_info.state == PA_SOURCE_INIT) {
 | 
				
			||||||
 | 
					                if (build_pollfd(u) < 0)
 | 
				
			||||||
 | 
					                    /* FIXME: This will cause an assertion failure, because
 | 
				
			||||||
 | 
					                     * with the current design pa_source_put() is not allowed
 | 
				
			||||||
 | 
					                     * to fail and pa_source_put() has no fallback code that
 | 
				
			||||||
 | 
					                     * would start the source suspended if opening the device
 | 
				
			||||||
 | 
					                     * fails. */
 | 
				
			||||||
 | 
					                    return -PA_ERR_IO;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (u->source->thread_info.state == PA_SOURCE_SUSPENDED) {
 | 
				
			||||||
 | 
					                if ((r = unsuspend(u)) < 0)
 | 
				
			||||||
 | 
					                    return r;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case PA_SOURCE_UNLINKED:
 | 
				
			||||||
 | 
					        case PA_SOURCE_INIT:
 | 
				
			||||||
 | 
					        case PA_SOURCE_INVALID_STATE:
 | 
				
			||||||
 | 
					            ;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int ctl_mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
 | 
					static int ctl_mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
 | 
				
			||||||
    struct userdata *u = snd_mixer_elem_get_callback_private(elem);
 | 
					    struct userdata *u = snd_mixer_elem_get_callback_private(elem);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2036,6 +2044,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
 | 
				
			||||||
    if (u->use_tsched)
 | 
					    if (u->use_tsched)
 | 
				
			||||||
        u->source->update_requested_latency = source_update_requested_latency_cb;
 | 
					        u->source->update_requested_latency = source_update_requested_latency_cb;
 | 
				
			||||||
    u->source->set_state_in_main_thread = source_set_state_in_main_thread_cb;
 | 
					    u->source->set_state_in_main_thread = source_set_state_in_main_thread_cb;
 | 
				
			||||||
 | 
					    u->source->set_state_in_io_thread = source_set_state_in_io_thread_cb;
 | 
				
			||||||
    if (u->ucm_context)
 | 
					    if (u->ucm_context)
 | 
				
			||||||
        u->source->set_port = source_set_port_ucm_cb;
 | 
					        u->source->set_port = source_set_port_ucm_cb;
 | 
				
			||||||
    else
 | 
					    else
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -386,9 +386,40 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    switch (code) {
 | 
					    switch (code) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        case PA_SINK_MESSAGE_SET_STATE:
 | 
					        case PA_SINK_MESSAGE_GET_LATENCY: {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
 | 
					            if (u->read_smoother) {
 | 
				
			||||||
 | 
					                int64_t wi, ri;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                ri = pa_smoother_get(u->read_smoother, pa_rtclock_now());
 | 
				
			||||||
 | 
					                wi = pa_bytes_to_usec(u->write_index + u->write_block_size, &u->sample_spec);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                *((int64_t*) data) = wi - ri;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                int64_t ri, wi;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                ri = pa_rtclock_now() - u->started_at;
 | 
				
			||||||
 | 
					                wi = pa_bytes_to_usec(u->write_index, &u->sample_spec);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                *((int64_t*) data) = wi - ri;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            *((int64_t*) data) += u->sink->thread_info.fixed_latency;
 | 
				
			||||||
 | 
					            return 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return pa_sink_process_msg(o, code, data, offset, chunk);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Called from the IO thread. */
 | 
				
			||||||
 | 
					static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state) {
 | 
				
			||||||
 | 
					    struct userdata *u;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pa_assert(s);
 | 
				
			||||||
 | 
					    pa_assert_se(u = s->userdata);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    switch (new_state) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        case PA_SINK_SUSPENDED:
 | 
					        case PA_SINK_SUSPENDED:
 | 
				
			||||||
            /* Ignore if transition is PA_SINK_INIT->PA_SINK_SUSPENDED */
 | 
					            /* Ignore if transition is PA_SINK_INIT->PA_SINK_SUSPENDED */
 | 
				
			||||||
| 
						 | 
					@ -421,35 +452,11 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
 | 
				
			||||||
        case PA_SINK_UNLINKED:
 | 
					        case PA_SINK_UNLINKED:
 | 
				
			||||||
        case PA_SINK_INIT:
 | 
					        case PA_SINK_INIT:
 | 
				
			||||||
        case PA_SINK_INVALID_STATE:
 | 
					        case PA_SINK_INVALID_STATE:
 | 
				
			||||||
                    ;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
 | 
					 | 
				
			||||||
        case PA_SINK_MESSAGE_GET_LATENCY: {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (u->read_smoother) {
 | 
					 | 
				
			||||||
                int64_t wi, ri;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                ri = pa_smoother_get(u->read_smoother, pa_rtclock_now());
 | 
					 | 
				
			||||||
                wi = pa_bytes_to_usec(u->write_index + u->write_block_size, &u->sample_spec);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                *((int64_t*) data) = wi - ri;
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                int64_t ri, wi;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                ri = pa_rtclock_now() - u->started_at;
 | 
					 | 
				
			||||||
                wi = pa_bytes_to_usec(u->write_index, &u->sample_spec);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                *((int64_t*) data) = wi - ri;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            *((int64_t*) data) += u->sink->thread_info.fixed_latency;
 | 
					 | 
				
			||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return pa_sink_process_msg(o, code, data, offset, chunk);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Run from IO thread */
 | 
					/* Run from IO thread */
 | 
				
			||||||
static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
 | 
					static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
 | 
				
			||||||
| 
						 | 
					@ -460,9 +467,33 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    switch (code) {
 | 
					    switch (code) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        case PA_SOURCE_MESSAGE_SET_STATE:
 | 
					        case PA_SOURCE_MESSAGE_GET_LATENCY: {
 | 
				
			||||||
 | 
					            int64_t wi, ri;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) {
 | 
					            if (u->read_smoother) {
 | 
				
			||||||
 | 
					                wi = pa_smoother_get(u->read_smoother, pa_rtclock_now());
 | 
				
			||||||
 | 
					                ri = pa_bytes_to_usec(u->read_index, &u->sample_spec);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                *((int64_t*) data) = wi - ri + u->source->thread_info.fixed_latency;
 | 
				
			||||||
 | 
					            } else
 | 
				
			||||||
 | 
					                *((int64_t*) data) = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return pa_source_process_msg(o, code, data, offset, chunk);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Called from the IO thread. */
 | 
				
			||||||
 | 
					static int source_set_state_in_io_thread_cb(pa_source *s, pa_source_state_t new_state) {
 | 
				
			||||||
 | 
					    struct userdata *u;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pa_assert(s);
 | 
				
			||||||
 | 
					    pa_assert_se(u = s->userdata);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    switch (new_state) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        case PA_SOURCE_SUSPENDED:
 | 
					        case PA_SOURCE_SUSPENDED:
 | 
				
			||||||
            /* Ignore if transition is PA_SOURCE_INIT->PA_SOURCE_SUSPENDED */
 | 
					            /* Ignore if transition is PA_SOURCE_INIT->PA_SOURCE_SUSPENDED */
 | 
				
			||||||
| 
						 | 
					@ -496,29 +527,12 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
 | 
				
			||||||
        case PA_SOURCE_UNLINKED:
 | 
					        case PA_SOURCE_UNLINKED:
 | 
				
			||||||
        case PA_SOURCE_INIT:
 | 
					        case PA_SOURCE_INIT:
 | 
				
			||||||
        case PA_SOURCE_INVALID_STATE:
 | 
					        case PA_SOURCE_INVALID_STATE:
 | 
				
			||||||
                    ;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
        case PA_SOURCE_MESSAGE_GET_LATENCY: {
 | 
					 | 
				
			||||||
            int64_t wi, ri;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (u->read_smoother) {
 | 
					 | 
				
			||||||
                wi = pa_smoother_get(u->read_smoother, pa_rtclock_now());
 | 
					 | 
				
			||||||
                ri = pa_bytes_to_usec(u->read_index, &u->sample_spec);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                *((int64_t*) data) = wi - ri + u->source->thread_info.fixed_latency;
 | 
					 | 
				
			||||||
            } else
 | 
					 | 
				
			||||||
                *((int64_t*) data) = 0;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return pa_source_process_msg(o, code, data, offset, chunk);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Called from main thread context */
 | 
					/* Called from main thread context */
 | 
				
			||||||
static int device_process_msg(pa_msgobject *obj, int code, void *data, int64_t offset, pa_memchunk *chunk) {
 | 
					static int device_process_msg(pa_msgobject *obj, int code, void *data, int64_t offset, pa_memchunk *chunk) {
 | 
				
			||||||
    struct bluetooth_msg *u = BLUETOOTH_MSG(obj);
 | 
					    struct bluetooth_msg *u = BLUETOOTH_MSG(obj);
 | 
				
			||||||
| 
						 | 
					@ -1591,6 +1605,7 @@ static int add_sink(struct userdata *u) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        u->sink->userdata = u;
 | 
					        u->sink->userdata = u;
 | 
				
			||||||
        u->sink->parent.process_msg = sink_process_msg;
 | 
					        u->sink->parent.process_msg = sink_process_msg;
 | 
				
			||||||
 | 
					        u->sink->set_state_in_io_thread = sink_set_state_in_io_thread_cb;
 | 
				
			||||||
        u->sink->set_port = sink_set_port_cb;
 | 
					        u->sink->set_port = sink_set_port_cb;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1663,6 +1678,7 @@ static int add_source(struct userdata *u) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        u->source->userdata = u;
 | 
					        u->source->userdata = u;
 | 
				
			||||||
        u->source->parent.process_msg = source_process_msg;
 | 
					        u->source->parent.process_msg = source_process_msg;
 | 
				
			||||||
 | 
					        u->source->set_state_in_io_thread = source_set_state_in_io_thread_cb;
 | 
				
			||||||
        u->source->set_port = source_set_port_cb;
 | 
					        u->source->set_port = source_set_port_cb;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -891,48 +891,6 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    switch (code) {
 | 
					    switch (code) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        case PA_SOURCE_MESSAGE_SET_STATE:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                case PA_SOURCE_SUSPENDED:
 | 
					 | 
				
			||||||
                    /* Ignore if transition is PA_SOURCE_INIT->PA_SOURCE_SUSPENDED */
 | 
					 | 
				
			||||||
                    if (!PA_SOURCE_IS_OPENED(u->source->thread_info.state))
 | 
					 | 
				
			||||||
                        break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    /* Stop the device if the sink is suspended as well */
 | 
					 | 
				
			||||||
                    if (!u->sink || u->sink->state == PA_SINK_SUSPENDED)
 | 
					 | 
				
			||||||
                        transport_release(u);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    if (u->read_smoother)
 | 
					 | 
				
			||||||
                        pa_smoother_pause(u->read_smoother, pa_rtclock_now());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                case PA_SOURCE_IDLE:
 | 
					 | 
				
			||||||
                case PA_SOURCE_RUNNING:
 | 
					 | 
				
			||||||
                    if (u->source->thread_info.state != PA_SOURCE_SUSPENDED)
 | 
					 | 
				
			||||||
                        break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    /* Resume the device if the sink was suspended as well */
 | 
					 | 
				
			||||||
                    if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
 | 
					 | 
				
			||||||
                        if (!setup_transport_and_stream(u))
 | 
					 | 
				
			||||||
                            return -1;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    /* We don't resume the smoother here. Instead we
 | 
					 | 
				
			||||||
                     * wait until the first packet arrives */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                case PA_SOURCE_UNLINKED:
 | 
					 | 
				
			||||||
                case PA_SOURCE_INIT:
 | 
					 | 
				
			||||||
                case PA_SOURCE_INVALID_STATE:
 | 
					 | 
				
			||||||
                    break;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        case PA_SOURCE_MESSAGE_GET_LATENCY: {
 | 
					        case PA_SOURCE_MESSAGE_GET_LATENCY: {
 | 
				
			||||||
            int64_t wi, ri;
 | 
					            int64_t wi, ri;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -956,6 +914,53 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
 | 
				
			||||||
    return pa_source_process_msg(o, code, data, offset, chunk);
 | 
					    return pa_source_process_msg(o, code, data, offset, chunk);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Called from the IO thread. */
 | 
				
			||||||
 | 
					static int source_set_state_in_io_thread_cb(pa_source *s, pa_source_state_t new_state) {
 | 
				
			||||||
 | 
					    struct userdata *u;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pa_assert(s);
 | 
				
			||||||
 | 
					    pa_assert_se(u = s->userdata);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    switch (new_state) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case PA_SOURCE_SUSPENDED:
 | 
				
			||||||
 | 
					            /* Ignore if transition is PA_SOURCE_INIT->PA_SOURCE_SUSPENDED */
 | 
				
			||||||
 | 
					            if (!PA_SOURCE_IS_OPENED(u->source->thread_info.state))
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            /* Stop the device if the sink is suspended as well */
 | 
				
			||||||
 | 
					            if (!u->sink || u->sink->state == PA_SINK_SUSPENDED)
 | 
				
			||||||
 | 
					                transport_release(u);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (u->read_smoother)
 | 
				
			||||||
 | 
					                pa_smoother_pause(u->read_smoother, pa_rtclock_now());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case PA_SOURCE_IDLE:
 | 
				
			||||||
 | 
					        case PA_SOURCE_RUNNING:
 | 
				
			||||||
 | 
					            if (u->source->thread_info.state != PA_SOURCE_SUSPENDED)
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            /* Resume the device if the sink was suspended as well */
 | 
				
			||||||
 | 
					            if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state))
 | 
				
			||||||
 | 
					                if (!setup_transport_and_stream(u))
 | 
				
			||||||
 | 
					                    return -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            /* We don't resume the smoother here. Instead we
 | 
				
			||||||
 | 
					             * wait until the first packet arrives */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case PA_SOURCE_UNLINKED:
 | 
				
			||||||
 | 
					        case PA_SOURCE_INIT:
 | 
				
			||||||
 | 
					        case PA_SOURCE_INVALID_STATE:
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Run from main thread */
 | 
					/* Run from main thread */
 | 
				
			||||||
static void source_set_volume_cb(pa_source *s) {
 | 
					static void source_set_volume_cb(pa_source *s) {
 | 
				
			||||||
    uint16_t gain;
 | 
					    uint16_t gain;
 | 
				
			||||||
| 
						 | 
					@ -1044,6 +1049,7 @@ static int add_source(struct userdata *u) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    u->source->userdata = u;
 | 
					    u->source->userdata = u;
 | 
				
			||||||
    u->source->parent.process_msg = source_process_msg;
 | 
					    u->source->parent.process_msg = source_process_msg;
 | 
				
			||||||
 | 
					    u->source->set_state_in_io_thread = source_set_state_in_io_thread_cb;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) {
 | 
					    if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) {
 | 
				
			||||||
        pa_source_set_set_volume_callback(u->source, source_set_volume_cb);
 | 
					        pa_source_set_set_volume_callback(u->source, source_set_volume_cb);
 | 
				
			||||||
| 
						 | 
					@ -1061,45 +1067,6 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    switch (code) {
 | 
					    switch (code) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        case PA_SINK_MESSAGE_SET_STATE:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                case PA_SINK_SUSPENDED:
 | 
					 | 
				
			||||||
                    /* Ignore if transition is PA_SINK_INIT->PA_SINK_SUSPENDED */
 | 
					 | 
				
			||||||
                    if (!PA_SINK_IS_OPENED(u->sink->thread_info.state))
 | 
					 | 
				
			||||||
                        break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    /* Stop the device if the source is suspended as well */
 | 
					 | 
				
			||||||
                    if (!u->source || u->source->state == PA_SOURCE_SUSPENDED)
 | 
					 | 
				
			||||||
                        /* We deliberately ignore whether stopping
 | 
					 | 
				
			||||||
                         * actually worked. Since the stream_fd is
 | 
					 | 
				
			||||||
                         * closed it doesn't really matter */
 | 
					 | 
				
			||||||
                        transport_release(u);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                case PA_SINK_IDLE:
 | 
					 | 
				
			||||||
                case PA_SINK_RUNNING:
 | 
					 | 
				
			||||||
                    if (u->sink->thread_info.state != PA_SINK_SUSPENDED)
 | 
					 | 
				
			||||||
                        break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    /* Resume the device if the source was suspended as well */
 | 
					 | 
				
			||||||
                    if (!u->source || !PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
 | 
					 | 
				
			||||||
                        if (!setup_transport_and_stream(u))
 | 
					 | 
				
			||||||
                            return -1;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                case PA_SINK_UNLINKED:
 | 
					 | 
				
			||||||
                case PA_SINK_INIT:
 | 
					 | 
				
			||||||
                case PA_SINK_INVALID_STATE:
 | 
					 | 
				
			||||||
                    break;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        case PA_SINK_MESSAGE_GET_LATENCY: {
 | 
					        case PA_SINK_MESSAGE_GET_LATENCY: {
 | 
				
			||||||
            int64_t wi, ri;
 | 
					            int64_t wi, ri;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1124,6 +1091,50 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
 | 
				
			||||||
    return pa_sink_process_msg(o, code, data, offset, chunk);
 | 
					    return pa_sink_process_msg(o, code, data, offset, chunk);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Called from the IO thread. */
 | 
				
			||||||
 | 
					static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state) {
 | 
				
			||||||
 | 
					    struct userdata *u;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pa_assert(s);
 | 
				
			||||||
 | 
					    pa_assert_se(u = s->userdata);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    switch (new_state) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case PA_SINK_SUSPENDED:
 | 
				
			||||||
 | 
					            /* Ignore if transition is PA_SINK_INIT->PA_SINK_SUSPENDED */
 | 
				
			||||||
 | 
					            if (!PA_SINK_IS_OPENED(u->sink->thread_info.state))
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            /* Stop the device if the source is suspended as well */
 | 
				
			||||||
 | 
					            if (!u->source || u->source->state == PA_SOURCE_SUSPENDED)
 | 
				
			||||||
 | 
					                /* We deliberately ignore whether stopping
 | 
				
			||||||
 | 
					                 * actually worked. Since the stream_fd is
 | 
				
			||||||
 | 
					                 * closed it doesn't really matter */
 | 
				
			||||||
 | 
					                transport_release(u);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case PA_SINK_IDLE:
 | 
				
			||||||
 | 
					        case PA_SINK_RUNNING:
 | 
				
			||||||
 | 
					            if (u->sink->thread_info.state != PA_SINK_SUSPENDED)
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            /* Resume the device if the source was suspended as well */
 | 
				
			||||||
 | 
					            if (!u->source || !PA_SOURCE_IS_OPENED(u->source->thread_info.state))
 | 
				
			||||||
 | 
					                if (!setup_transport_and_stream(u))
 | 
				
			||||||
 | 
					                    return -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case PA_SINK_UNLINKED:
 | 
				
			||||||
 | 
					        case PA_SINK_INIT:
 | 
				
			||||||
 | 
					        case PA_SINK_INVALID_STATE:
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Run from main thread */
 | 
					/* Run from main thread */
 | 
				
			||||||
static void sink_set_volume_cb(pa_sink *s) {
 | 
					static void sink_set_volume_cb(pa_sink *s) {
 | 
				
			||||||
    uint16_t gain;
 | 
					    uint16_t gain;
 | 
				
			||||||
| 
						 | 
					@ -1213,6 +1224,7 @@ static int add_sink(struct userdata *u) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    u->sink->userdata = u;
 | 
					    u->sink->userdata = u;
 | 
				
			||||||
    u->sink->parent.process_msg = sink_process_msg;
 | 
					    u->sink->parent.process_msg = sink_process_msg;
 | 
				
			||||||
 | 
					    u->sink->set_state_in_io_thread = sink_set_state_in_io_thread_cb;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) {
 | 
					    if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) {
 | 
				
			||||||
        pa_sink_set_set_volume_callback(u->sink, sink_set_volume_cb);
 | 
					        pa_sink_set_set_volume_callback(u->sink, sink_set_volume_cb);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -458,19 +458,6 @@ static int sink_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t of
 | 
				
			||||||
                pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->sink_input->sink->sample_spec);
 | 
					                pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->sink_input->sink->sample_spec);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return 0;
 | 
					            return 0;
 | 
				
			||||||
 | 
					 | 
				
			||||||
        case PA_SINK_MESSAGE_SET_STATE: {
 | 
					 | 
				
			||||||
            pa_sink_state_t new_state = (pa_sink_state_t) PA_PTR_TO_UINT(data);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            /* When set to running or idle for the first time, request a rewind
 | 
					 | 
				
			||||||
             * of the master sink to make sure we are heard immediately */
 | 
					 | 
				
			||||||
            if ((new_state == PA_SINK_IDLE || new_state == PA_SINK_RUNNING) && u->sink->thread_info.state == PA_SINK_INIT) {
 | 
					 | 
				
			||||||
                pa_log_debug("Requesting rewind due to state change.");
 | 
					 | 
				
			||||||
                pa_sink_input_request_rewind(u->sink_input, 0, false, true, true);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return pa_sink_process_msg(o, code, data, offset, chunk);
 | 
					    return pa_sink_process_msg(o, code, data, offset, chunk);
 | 
				
			||||||
| 
						 | 
					@ -526,6 +513,23 @@ static int sink_set_state_in_main_thread_cb(pa_sink *s, pa_sink_state_t state, p
 | 
				
			||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Called from the IO thread. */
 | 
				
			||||||
 | 
					static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state) {
 | 
				
			||||||
 | 
					    struct userdata *u;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pa_assert(s);
 | 
				
			||||||
 | 
					    pa_assert_se(u = s->userdata);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* When set to running or idle for the first time, request a rewind
 | 
				
			||||||
 | 
					     * of the master sink to make sure we are heard immediately */
 | 
				
			||||||
 | 
					    if ((new_state == PA_SINK_IDLE || new_state == PA_SINK_RUNNING) && u->sink->thread_info.state == PA_SINK_INIT) {
 | 
				
			||||||
 | 
					        pa_log_debug("Requesting rewind due to state change.");
 | 
				
			||||||
 | 
					        pa_sink_input_request_rewind(u->sink_input, 0, false, true, true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Called from source I/O thread context */
 | 
					/* Called from source I/O thread context */
 | 
				
			||||||
static void source_update_requested_latency_cb(pa_source *s) {
 | 
					static void source_update_requested_latency_cb(pa_source *s) {
 | 
				
			||||||
    struct userdata *u;
 | 
					    struct userdata *u;
 | 
				
			||||||
| 
						 | 
					@ -1926,6 +1930,7 @@ int pa__init(pa_module*m) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    u->sink->parent.process_msg = sink_process_msg_cb;
 | 
					    u->sink->parent.process_msg = sink_process_msg_cb;
 | 
				
			||||||
    u->sink->set_state_in_main_thread = sink_set_state_in_main_thread_cb;
 | 
					    u->sink->set_state_in_main_thread = sink_set_state_in_main_thread_cb;
 | 
				
			||||||
 | 
					    u->sink->set_state_in_io_thread = sink_set_state_in_io_thread_cb;
 | 
				
			||||||
    u->sink->update_requested_latency = sink_update_requested_latency_cb;
 | 
					    u->sink->update_requested_latency = sink_update_requested_latency_cb;
 | 
				
			||||||
    u->sink->request_rewind = sink_request_rewind_cb;
 | 
					    u->sink->request_rewind = sink_request_rewind_cb;
 | 
				
			||||||
    pa_sink_set_set_mute_callback(u->sink, sink_set_mute_cb);
 | 
					    pa_sink_set_set_mute_callback(u->sink, sink_set_mute_cb);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -718,6 +718,25 @@ static int sink_set_state_in_main_thread_cb(pa_sink *sink, pa_sink_state_t state
 | 
				
			||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Called from the IO thread. */
 | 
				
			||||||
 | 
					static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state) {
 | 
				
			||||||
 | 
					    struct userdata *u;
 | 
				
			||||||
 | 
					    bool running;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pa_assert(s);
 | 
				
			||||||
 | 
					    pa_assert_se(u = s->userdata);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    running = new_state == PA_SINK_RUNNING;
 | 
				
			||||||
 | 
					    pa_atomic_store(&u->thread_info.running, running);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (running)
 | 
				
			||||||
 | 
					        pa_smoother_resume(u->thread_info.smoother, pa_rtclock_now(), true);
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					        pa_smoother_pause(u->thread_info.smoother, pa_rtclock_now());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Called from IO context */
 | 
					/* Called from IO context */
 | 
				
			||||||
static void update_max_request(struct userdata *u) {
 | 
					static void update_max_request(struct userdata *u) {
 | 
				
			||||||
    size_t max_request = 0;
 | 
					    size_t max_request = 0;
 | 
				
			||||||
| 
						 | 
					@ -859,19 +878,6 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    switch (code) {
 | 
					    switch (code) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        case PA_SINK_MESSAGE_SET_STATE: {
 | 
					 | 
				
			||||||
            bool running = (PA_PTR_TO_UINT(data) == PA_SINK_RUNNING);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            pa_atomic_store(&u->thread_info.running, running);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (running)
 | 
					 | 
				
			||||||
                pa_smoother_resume(u->thread_info.smoother, pa_rtclock_now(), true);
 | 
					 | 
				
			||||||
            else
 | 
					 | 
				
			||||||
                pa_smoother_pause(u->thread_info.smoother, pa_rtclock_now());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        case PA_SINK_MESSAGE_GET_LATENCY: {
 | 
					        case PA_SINK_MESSAGE_GET_LATENCY: {
 | 
				
			||||||
            pa_usec_t x, y, c;
 | 
					            pa_usec_t x, y, c;
 | 
				
			||||||
            int64_t *delay = data;
 | 
					            int64_t *delay = data;
 | 
				
			||||||
| 
						 | 
					@ -1426,6 +1432,7 @@ int pa__init(pa_module*m) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    u->sink->parent.process_msg = sink_process_msg;
 | 
					    u->sink->parent.process_msg = sink_process_msg;
 | 
				
			||||||
    u->sink->set_state_in_main_thread = sink_set_state_in_main_thread_cb;
 | 
					    u->sink->set_state_in_main_thread = sink_set_state_in_main_thread_cb;
 | 
				
			||||||
 | 
					    u->sink->set_state_in_io_thread = sink_set_state_in_io_thread_cb;
 | 
				
			||||||
    u->sink->update_requested_latency = sink_update_requested_latency;
 | 
					    u->sink->update_requested_latency = sink_update_requested_latency;
 | 
				
			||||||
    u->sink->userdata = u;
 | 
					    u->sink->userdata = u;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -267,18 +267,6 @@ static int sink_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t of
 | 
				
			||||||
            //+ pa_bytes_to_usec(u->latency * fs, ss)
 | 
					            //+ pa_bytes_to_usec(u->latency * fs, ss)
 | 
				
			||||||
            return 0;
 | 
					            return 0;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					 | 
				
			||||||
        case PA_SINK_MESSAGE_SET_STATE: {
 | 
					 | 
				
			||||||
            pa_sink_state_t new_state = (pa_sink_state_t) PA_PTR_TO_UINT(data);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            /* When set to running or idle for the first time, request a rewind
 | 
					 | 
				
			||||||
             * of the master sink to make sure we are heard immediately */
 | 
					 | 
				
			||||||
            if ((new_state == PA_SINK_IDLE || new_state == PA_SINK_RUNNING) && u->sink->thread_info.state == PA_SINK_INIT) {
 | 
					 | 
				
			||||||
                pa_log_debug("Requesting rewind due to state change.");
 | 
					 | 
				
			||||||
                pa_sink_input_request_rewind(u->sink_input, 0, false, true, true);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return pa_sink_process_msg(o, code, data, offset, chunk);
 | 
					    return pa_sink_process_msg(o, code, data, offset, chunk);
 | 
				
			||||||
| 
						 | 
					@ -299,6 +287,23 @@ static int sink_set_state_in_main_thread_cb(pa_sink *s, pa_sink_state_t state, p
 | 
				
			||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Called from the IO thread. */
 | 
				
			||||||
 | 
					static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state) {
 | 
				
			||||||
 | 
					    struct userdata *u;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pa_assert(s);
 | 
				
			||||||
 | 
					    pa_assert_se(u = s->userdata);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* When set to running or idle for the first time, request a rewind
 | 
				
			||||||
 | 
					     * of the master sink to make sure we are heard immediately */
 | 
				
			||||||
 | 
					    if ((new_state == PA_SINK_IDLE || new_state == PA_SINK_RUNNING) && u->sink->thread_info.state == PA_SINK_INIT) {
 | 
				
			||||||
 | 
					        pa_log_debug("Requesting rewind due to state change.");
 | 
				
			||||||
 | 
					        pa_sink_input_request_rewind(u->sink_input, 0, false, true, true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Called from I/O thread context */
 | 
					/* Called from I/O thread context */
 | 
				
			||||||
static void sink_request_rewind_cb(pa_sink *s) {
 | 
					static void sink_request_rewind_cb(pa_sink *s) {
 | 
				
			||||||
    struct userdata *u;
 | 
					    struct userdata *u;
 | 
				
			||||||
| 
						 | 
					@ -1230,6 +1235,7 @@ int pa__init(pa_module*m) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    u->sink->parent.process_msg = sink_process_msg_cb;
 | 
					    u->sink->parent.process_msg = sink_process_msg_cb;
 | 
				
			||||||
    u->sink->set_state_in_main_thread = sink_set_state_in_main_thread_cb;
 | 
					    u->sink->set_state_in_main_thread = sink_set_state_in_main_thread_cb;
 | 
				
			||||||
 | 
					    u->sink->set_state_in_io_thread = sink_set_state_in_io_thread_cb;
 | 
				
			||||||
    u->sink->update_requested_latency = sink_update_requested_latency_cb;
 | 
					    u->sink->update_requested_latency = sink_update_requested_latency_cb;
 | 
				
			||||||
    u->sink->request_rewind = sink_request_rewind_cb;
 | 
					    u->sink->request_rewind = sink_request_rewind_cb;
 | 
				
			||||||
    pa_sink_set_set_mute_callback(u->sink, sink_set_mute_cb);
 | 
					    pa_sink_set_set_mute_callback(u->sink, sink_set_mute_cb);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -141,32 +141,6 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    switch (code) {
 | 
					    switch (code) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        case PA_SINK_MESSAGE_SET_STATE:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                case PA_SINK_SUSPENDED:
 | 
					 | 
				
			||||||
                    pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    pa_smoother_pause(u->smoother, pa_rtclock_now());
 | 
					 | 
				
			||||||
                    break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                case PA_SINK_IDLE:
 | 
					 | 
				
			||||||
                case PA_SINK_RUNNING:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    if (u->sink->thread_info.state == PA_SINK_SUSPENDED)
 | 
					 | 
				
			||||||
                        pa_smoother_resume(u->smoother, pa_rtclock_now(), true);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                case PA_SINK_UNLINKED:
 | 
					 | 
				
			||||||
                case PA_SINK_INIT:
 | 
					 | 
				
			||||||
                case PA_SINK_INVALID_STATE:
 | 
					 | 
				
			||||||
                    ;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        case PA_SINK_MESSAGE_GET_LATENCY: {
 | 
					        case PA_SINK_MESSAGE_GET_LATENCY: {
 | 
				
			||||||
            pa_usec_t w, r;
 | 
					            pa_usec_t w, r;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -194,6 +168,38 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
 | 
				
			||||||
    return pa_sink_process_msg(o, code, data, offset, chunk);
 | 
					    return pa_sink_process_msg(o, code, data, offset, chunk);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Called from the IO thread. */
 | 
				
			||||||
 | 
					static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state) {
 | 
				
			||||||
 | 
					    struct userdata *u;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pa_assert(s);
 | 
				
			||||||
 | 
					    pa_assert_se(u = s->userdata);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    switch (new_state) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case PA_SINK_SUSPENDED:
 | 
				
			||||||
 | 
					            pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            pa_smoother_pause(u->smoother, pa_rtclock_now());
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case PA_SINK_IDLE:
 | 
				
			||||||
 | 
					        case PA_SINK_RUNNING:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (u->sink->thread_info.state == PA_SINK_SUSPENDED)
 | 
				
			||||||
 | 
					                pa_smoother_resume(u->smoother, pa_rtclock_now(), true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case PA_SINK_UNLINKED:
 | 
				
			||||||
 | 
					        case PA_SINK_INIT:
 | 
				
			||||||
 | 
					        case PA_SINK_INVALID_STATE:
 | 
				
			||||||
 | 
					            ;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void thread_func(void *userdata) {
 | 
					static void thread_func(void *userdata) {
 | 
				
			||||||
    struct userdata *u = userdata;
 | 
					    struct userdata *u = userdata;
 | 
				
			||||||
    int write_type = 0;
 | 
					    int write_type = 0;
 | 
				
			||||||
| 
						 | 
					@ -611,6 +617,7 @@ int pa__init(pa_module*m) {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    u->sink->parent.process_msg = sink_process_msg;
 | 
					    u->sink->parent.process_msg = sink_process_msg;
 | 
				
			||||||
 | 
					    u->sink->set_state_in_io_thread = sink_set_state_in_io_thread_cb;
 | 
				
			||||||
    u->sink->userdata = u;
 | 
					    u->sink->userdata = u;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
 | 
					    pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -374,18 +374,6 @@ static int sink_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t of
 | 
				
			||||||
        connect_control_ports(u);
 | 
					        connect_control_ports(u);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return 0;
 | 
					        return 0;
 | 
				
			||||||
 | 
					 | 
				
			||||||
        case PA_SINK_MESSAGE_SET_STATE: {
 | 
					 | 
				
			||||||
            pa_sink_state_t new_state = (pa_sink_state_t) PA_PTR_TO_UINT(data);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            /* When set to running or idle for the first time, request a rewind
 | 
					 | 
				
			||||||
             * of the master sink to make sure we are heard immediately */
 | 
					 | 
				
			||||||
            if ((new_state == PA_SINK_IDLE || new_state == PA_SINK_RUNNING) && u->sink->thread_info.state == PA_SINK_INIT) {
 | 
					 | 
				
			||||||
                pa_log_debug("Requesting rewind due to state change.");
 | 
					 | 
				
			||||||
                pa_sink_input_request_rewind(u->sink_input, 0, false, true, true);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return pa_sink_process_msg(o, code, data, offset, chunk);
 | 
					    return pa_sink_process_msg(o, code, data, offset, chunk);
 | 
				
			||||||
| 
						 | 
					@ -406,6 +394,23 @@ static int sink_set_state_in_main_thread_cb(pa_sink *s, pa_sink_state_t state, p
 | 
				
			||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Called from the IO thread. */
 | 
				
			||||||
 | 
					static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state) {
 | 
				
			||||||
 | 
					    struct userdata *u;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pa_assert(s);
 | 
				
			||||||
 | 
					    pa_assert_se(u = s->userdata);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* When set to running or idle for the first time, request a rewind
 | 
				
			||||||
 | 
					     * of the master sink to make sure we are heard immediately */
 | 
				
			||||||
 | 
					    if ((new_state == PA_SINK_IDLE || new_state == PA_SINK_RUNNING) && u->sink->thread_info.state == PA_SINK_INIT) {
 | 
				
			||||||
 | 
					        pa_log_debug("Requesting rewind due to state change.");
 | 
				
			||||||
 | 
					        pa_sink_input_request_rewind(u->sink_input, 0, false, true, true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Called from I/O thread context */
 | 
					/* Called from I/O thread context */
 | 
				
			||||||
static void sink_request_rewind_cb(pa_sink *s) {
 | 
					static void sink_request_rewind_cb(pa_sink *s) {
 | 
				
			||||||
    struct userdata *u;
 | 
					    struct userdata *u;
 | 
				
			||||||
| 
						 | 
					@ -1298,6 +1303,7 @@ int pa__init(pa_module*m) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    u->sink->parent.process_msg = sink_process_msg_cb;
 | 
					    u->sink->parent.process_msg = sink_process_msg_cb;
 | 
				
			||||||
    u->sink->set_state_in_main_thread = sink_set_state_in_main_thread_cb;
 | 
					    u->sink->set_state_in_main_thread = sink_set_state_in_main_thread_cb;
 | 
				
			||||||
 | 
					    u->sink->set_state_in_io_thread = sink_set_state_in_io_thread_cb;
 | 
				
			||||||
    u->sink->update_requested_latency = sink_update_requested_latency_cb;
 | 
					    u->sink->update_requested_latency = sink_update_requested_latency_cb;
 | 
				
			||||||
    u->sink->request_rewind = sink_request_rewind_cb;
 | 
					    u->sink->request_rewind = sink_request_rewind_cb;
 | 
				
			||||||
    pa_sink_set_set_mute_callback(u->sink, sink_set_mute_cb);
 | 
					    pa_sink_set_set_mute_callback(u->sink, sink_set_mute_cb);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -89,15 +89,6 @@ static int sink_process_msg(
 | 
				
			||||||
    struct userdata *u = PA_SINK(o)->userdata;
 | 
					    struct userdata *u = PA_SINK(o)->userdata;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    switch (code) {
 | 
					    switch (code) {
 | 
				
			||||||
        case PA_SINK_MESSAGE_SET_STATE:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (u->sink->thread_info.state == PA_SINK_SUSPENDED || u->sink->thread_info.state == PA_SINK_INIT) {
 | 
					 | 
				
			||||||
                if (PA_SINK_IS_OPENED(PA_PTR_TO_UINT(data)))
 | 
					 | 
				
			||||||
                    u->timestamp = pa_rtclock_now();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        case PA_SINK_MESSAGE_GET_LATENCY: {
 | 
					        case PA_SINK_MESSAGE_GET_LATENCY: {
 | 
				
			||||||
            pa_usec_t now;
 | 
					            pa_usec_t now;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -111,6 +102,21 @@ static int sink_process_msg(
 | 
				
			||||||
    return pa_sink_process_msg(o, code, data, offset, chunk);
 | 
					    return pa_sink_process_msg(o, code, data, offset, chunk);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Called from the IO thread. */
 | 
				
			||||||
 | 
					static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state) {
 | 
				
			||||||
 | 
					    struct userdata *u;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pa_assert(s);
 | 
				
			||||||
 | 
					    pa_assert_se(u = s->userdata);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (u->sink->thread_info.state == PA_SINK_SUSPENDED || u->sink->thread_info.state == PA_SINK_INIT) {
 | 
				
			||||||
 | 
					        if (PA_SINK_IS_OPENED(new_state))
 | 
				
			||||||
 | 
					            u->timestamp = pa_rtclock_now();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void sink_update_requested_latency_cb(pa_sink *s) {
 | 
					static void sink_update_requested_latency_cb(pa_sink *s) {
 | 
				
			||||||
    struct userdata *u;
 | 
					    struct userdata *u;
 | 
				
			||||||
    size_t nbytes;
 | 
					    size_t nbytes;
 | 
				
			||||||
| 
						 | 
					@ -297,6 +303,7 @@ int pa__init(pa_module*m) {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    u->sink->parent.process_msg = sink_process_msg;
 | 
					    u->sink->parent.process_msg = sink_process_msg;
 | 
				
			||||||
 | 
					    u->sink->set_state_in_io_thread = sink_set_state_in_io_thread_cb;
 | 
				
			||||||
    u->sink->update_requested_latency = sink_update_requested_latency_cb;
 | 
					    u->sink->update_requested_latency = sink_update_requested_latency_cb;
 | 
				
			||||||
    u->sink->userdata = u;
 | 
					    u->sink->userdata = u;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -89,13 +89,6 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
 | 
				
			||||||
    struct userdata *u = PA_SOURCE(o)->userdata;
 | 
					    struct userdata *u = PA_SOURCE(o)->userdata;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    switch (code) {
 | 
					    switch (code) {
 | 
				
			||||||
        case PA_SOURCE_MESSAGE_SET_STATE:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (PA_PTR_TO_UINT(data) == PA_SOURCE_RUNNING)
 | 
					 | 
				
			||||||
                u->timestamp = pa_rtclock_now();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        case PA_SOURCE_MESSAGE_GET_LATENCY: {
 | 
					        case PA_SOURCE_MESSAGE_GET_LATENCY: {
 | 
				
			||||||
            pa_usec_t now;
 | 
					            pa_usec_t now;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -109,6 +102,19 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
 | 
				
			||||||
    return pa_source_process_msg(o, code, data, offset, chunk);
 | 
					    return pa_source_process_msg(o, code, data, offset, chunk);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Called from the IO thread. */
 | 
				
			||||||
 | 
					static int source_set_state_in_io_thread_cb(pa_source *s, pa_source_state_t new_state) {
 | 
				
			||||||
 | 
					    struct userdata *u;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pa_assert(s);
 | 
				
			||||||
 | 
					    pa_assert_se(u = s->userdata);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (new_state == PA_SOURCE_RUNNING)
 | 
				
			||||||
 | 
					        u->timestamp = pa_rtclock_now();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void source_update_requested_latency_cb(pa_source *s) {
 | 
					static void source_update_requested_latency_cb(pa_source *s) {
 | 
				
			||||||
    struct userdata *u;
 | 
					    struct userdata *u;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -229,6 +235,7 @@ int pa__init(pa_module*m) {
 | 
				
			||||||
    u->latency_time = latency_time;
 | 
					    u->latency_time = latency_time;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    u->source->parent.process_msg = source_process_msg;
 | 
					    u->source->parent.process_msg = source_process_msg;
 | 
				
			||||||
 | 
					    u->source->set_state_in_io_thread = source_set_state_in_io_thread_cb;
 | 
				
			||||||
    u->source->update_requested_latency = source_update_requested_latency_cb;
 | 
					    u->source->update_requested_latency = source_update_requested_latency_cb;
 | 
				
			||||||
    u->source->userdata = u;
 | 
					    u->source->userdata = u;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -110,24 +110,6 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
 | 
				
			||||||
    struct userdata *u = PA_SINK(o)->userdata;
 | 
					    struct userdata *u = PA_SINK(o)->userdata;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    switch (code) {
 | 
					    switch (code) {
 | 
				
			||||||
        case PA_SINK_MESSAGE_SET_STATE:
 | 
					 | 
				
			||||||
            if (u->sink->thread_info.state == PA_SINK_SUSPENDED || u->sink->thread_info.state == PA_SINK_INIT) {
 | 
					 | 
				
			||||||
                if (PA_SINK_IS_OPENED(PA_PTR_TO_UINT(data)))
 | 
					 | 
				
			||||||
                    u->timestamp = pa_rtclock_now();
 | 
					 | 
				
			||||||
            } else if (u->sink->thread_info.state == PA_SINK_RUNNING || u->sink->thread_info.state == PA_SINK_IDLE) {
 | 
					 | 
				
			||||||
                if (PA_PTR_TO_UINT(data) == PA_SINK_SUSPENDED) {
 | 
					 | 
				
			||||||
                    /* Clear potential FIFO error flag */
 | 
					 | 
				
			||||||
                    u->fifo_error = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    /* Continuously dropping data (clear counter on entering suspended state. */
 | 
					 | 
				
			||||||
                    if (u->bytes_dropped != 0) {
 | 
					 | 
				
			||||||
                        pa_log_debug("Pipe-sink continuously dropping data - clear statistics (%zu -> 0 bytes dropped)", u->bytes_dropped);
 | 
					 | 
				
			||||||
                        u->bytes_dropped = 0;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        case PA_SINK_MESSAGE_GET_LATENCY:
 | 
					        case PA_SINK_MESSAGE_GET_LATENCY:
 | 
				
			||||||
            if (u->use_system_clock_for_timing) {
 | 
					            if (u->use_system_clock_for_timing) {
 | 
				
			||||||
                pa_usec_t now;
 | 
					                pa_usec_t now;
 | 
				
			||||||
| 
						 | 
					@ -153,6 +135,32 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
 | 
				
			||||||
    return pa_sink_process_msg(o, code, data, offset, chunk);
 | 
					    return pa_sink_process_msg(o, code, data, offset, chunk);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Called from the IO thread. */
 | 
				
			||||||
 | 
					static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state) {
 | 
				
			||||||
 | 
					    struct userdata *u;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pa_assert(s);
 | 
				
			||||||
 | 
					    pa_assert_se(u = s->userdata);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (u->sink->thread_info.state == PA_SINK_SUSPENDED || u->sink->thread_info.state == PA_SINK_INIT) {
 | 
				
			||||||
 | 
					        if (PA_SINK_IS_OPENED(new_state))
 | 
				
			||||||
 | 
					            u->timestamp = pa_rtclock_now();
 | 
				
			||||||
 | 
					    } else if (u->sink->thread_info.state == PA_SINK_RUNNING || u->sink->thread_info.state == PA_SINK_IDLE) {
 | 
				
			||||||
 | 
					        if (new_state == PA_SINK_SUSPENDED) {
 | 
				
			||||||
 | 
					            /* Clear potential FIFO error flag */
 | 
				
			||||||
 | 
					            u->fifo_error = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            /* Continuously dropping data (clear counter on entering suspended state. */
 | 
				
			||||||
 | 
					            if (u->bytes_dropped != 0) {
 | 
				
			||||||
 | 
					                pa_log_debug("Pipe-sink continuously dropping data - clear statistics (%zu -> 0 bytes dropped)", u->bytes_dropped);
 | 
				
			||||||
 | 
					                u->bytes_dropped = 0;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void sink_update_requested_latency_cb(pa_sink *s) {
 | 
					static void sink_update_requested_latency_cb(pa_sink *s) {
 | 
				
			||||||
    struct userdata *u;
 | 
					    struct userdata *u;
 | 
				
			||||||
    size_t nbytes;
 | 
					    size_t nbytes;
 | 
				
			||||||
| 
						 | 
					@ -505,6 +513,7 @@ int pa__init(pa_module *m) {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    u->sink->parent.process_msg = sink_process_msg;
 | 
					    u->sink->parent.process_msg = sink_process_msg;
 | 
				
			||||||
 | 
					    u->sink->set_state_in_io_thread = sink_set_state_in_io_thread_cb;
 | 
				
			||||||
    if (u->use_system_clock_for_timing)
 | 
					    if (u->use_system_clock_for_timing)
 | 
				
			||||||
        u->sink->update_requested_latency = sink_update_requested_latency_cb;
 | 
					        u->sink->update_requested_latency = sink_update_requested_latency_cb;
 | 
				
			||||||
    u->sink->userdata = u;
 | 
					    u->sink->userdata = u;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -94,18 +94,6 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
 | 
				
			||||||
                pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->sink_input->sink->sample_spec);
 | 
					                pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->sink_input->sink->sample_spec);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return 0;
 | 
					            return 0;
 | 
				
			||||||
 | 
					 | 
				
			||||||
        case PA_SINK_MESSAGE_SET_STATE: {
 | 
					 | 
				
			||||||
            pa_sink_state_t new_state = (pa_sink_state_t) PA_PTR_TO_UINT(data);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            /* When set to running or idle for the first time, request a rewind
 | 
					 | 
				
			||||||
             * of the master sink to make sure we are heard immediately */
 | 
					 | 
				
			||||||
            if ((new_state == PA_SINK_IDLE || new_state == PA_SINK_RUNNING) && u->sink->thread_info.state == PA_SINK_INIT) {
 | 
					 | 
				
			||||||
                pa_log_debug("Requesting rewind due to state change.");
 | 
					 | 
				
			||||||
                pa_sink_input_request_rewind(u->sink_input, 0, false, true, true);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return pa_sink_process_msg(o, code, data, offset, chunk);
 | 
					    return pa_sink_process_msg(o, code, data, offset, chunk);
 | 
				
			||||||
| 
						 | 
					@ -126,6 +114,23 @@ static int sink_set_state_in_main_thread(pa_sink *s, pa_sink_state_t state, pa_s
 | 
				
			||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Called from the IO thread. */
 | 
				
			||||||
 | 
					static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state) {
 | 
				
			||||||
 | 
					    struct userdata *u;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pa_assert(s);
 | 
				
			||||||
 | 
					    pa_assert_se(u = s->userdata);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* When set to running or idle for the first time, request a rewind
 | 
				
			||||||
 | 
					     * of the master sink to make sure we are heard immediately */
 | 
				
			||||||
 | 
					    if ((new_state == PA_SINK_IDLE || new_state == PA_SINK_RUNNING) && u->sink->thread_info.state == PA_SINK_INIT) {
 | 
				
			||||||
 | 
					        pa_log_debug("Requesting rewind due to state change.");
 | 
				
			||||||
 | 
					        pa_sink_input_request_rewind(u->sink_input, 0, false, true, true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Called from I/O thread context */
 | 
					/* Called from I/O thread context */
 | 
				
			||||||
static void sink_request_rewind(pa_sink *s) {
 | 
					static void sink_request_rewind(pa_sink *s) {
 | 
				
			||||||
    struct userdata *u;
 | 
					    struct userdata *u;
 | 
				
			||||||
| 
						 | 
					@ -411,6 +416,7 @@ int pa__init(pa_module*m) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    u->sink->parent.process_msg = sink_process_msg;
 | 
					    u->sink->parent.process_msg = sink_process_msg;
 | 
				
			||||||
    u->sink->set_state_in_main_thread = sink_set_state_in_main_thread;
 | 
					    u->sink->set_state_in_main_thread = sink_set_state_in_main_thread;
 | 
				
			||||||
 | 
					    u->sink->set_state_in_io_thread = sink_set_state_in_io_thread_cb;
 | 
				
			||||||
    u->sink->update_requested_latency = sink_update_requested_latency;
 | 
					    u->sink->update_requested_latency = sink_update_requested_latency;
 | 
				
			||||||
    u->sink->request_rewind = sink_request_rewind;
 | 
					    u->sink->request_rewind = sink_request_rewind;
 | 
				
			||||||
    u->sink->userdata = u;
 | 
					    u->sink->userdata = u;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -87,13 +87,6 @@ static int source_process_msg(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    switch (code) {
 | 
					    switch (code) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        case PA_SOURCE_MESSAGE_SET_STATE:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (PA_PTR_TO_UINT(data) == PA_SOURCE_RUNNING)
 | 
					 | 
				
			||||||
                u->timestamp = pa_rtclock_now();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        case PA_SOURCE_MESSAGE_GET_LATENCY: {
 | 
					        case PA_SOURCE_MESSAGE_GET_LATENCY: {
 | 
				
			||||||
            pa_usec_t now, left_to_fill;
 | 
					            pa_usec_t now, left_to_fill;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -109,6 +102,19 @@ static int source_process_msg(
 | 
				
			||||||
    return pa_source_process_msg(o, code, data, offset, chunk);
 | 
					    return pa_source_process_msg(o, code, data, offset, chunk);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Called from the IO thread. */
 | 
				
			||||||
 | 
					static int source_set_state_in_io_thread_cb(pa_source *s, pa_source_state_t new_state) {
 | 
				
			||||||
 | 
					    struct userdata *u;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pa_assert(s);
 | 
				
			||||||
 | 
					    pa_assert_se(u = s->userdata);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (new_state == PA_SOURCE_RUNNING)
 | 
				
			||||||
 | 
					        u->timestamp = pa_rtclock_now();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void source_update_requested_latency_cb(pa_source *s) {
 | 
					static void source_update_requested_latency_cb(pa_source *s) {
 | 
				
			||||||
    struct userdata *u;
 | 
					    struct userdata *u;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -257,6 +263,7 @@ int pa__init(pa_module*m) {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    u->source->parent.process_msg = source_process_msg;
 | 
					    u->source->parent.process_msg = source_process_msg;
 | 
				
			||||||
 | 
					    u->source->set_state_in_io_thread = source_set_state_in_io_thread_cb;
 | 
				
			||||||
    u->source->update_requested_latency = source_update_requested_latency_cb;
 | 
					    u->source->update_requested_latency = source_update_requested_latency_cb;
 | 
				
			||||||
    u->source->userdata = u;
 | 
					    u->source->userdata = u;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -390,10 +390,19 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
 | 
				
			||||||
        case PA_SINK_MESSAGE_GET_LATENCY:
 | 
					        case PA_SINK_MESSAGE_GET_LATENCY:
 | 
				
			||||||
            *((int64_t*) data) = sink_get_latency(u, &PA_SINK(o)->sample_spec);
 | 
					            *((int64_t*) data) = sink_get_latency(u, &PA_SINK(o)->sample_spec);
 | 
				
			||||||
            return 0;
 | 
					            return 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        case PA_SINK_MESSAGE_SET_STATE:
 | 
					    return pa_sink_process_msg(o, code, data, offset, chunk);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
 | 
					/* Called from the IO thread. */
 | 
				
			||||||
 | 
					static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state) {
 | 
				
			||||||
 | 
					    struct userdata *u;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pa_assert(s);
 | 
				
			||||||
 | 
					    pa_assert_se(u = s->userdata);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    switch (new_state) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        case PA_SINK_SUSPENDED:
 | 
					        case PA_SINK_SUSPENDED:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -431,10 +440,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
 | 
				
			||||||
            ;
 | 
					            ;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            break;
 | 
					    return 0;
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return pa_sink_process_msg(o, code, data, offset, chunk);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
 | 
					static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
 | 
				
			||||||
| 
						 | 
					@ -445,10 +451,19 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
 | 
				
			||||||
        case PA_SOURCE_MESSAGE_GET_LATENCY:
 | 
					        case PA_SOURCE_MESSAGE_GET_LATENCY:
 | 
				
			||||||
            *((pa_usec_t*) data) = source_get_latency(u, &PA_SOURCE(o)->sample_spec);
 | 
					            *((pa_usec_t*) data) = source_get_latency(u, &PA_SOURCE(o)->sample_spec);
 | 
				
			||||||
            return 0;
 | 
					            return 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        case PA_SOURCE_MESSAGE_SET_STATE:
 | 
					    return pa_source_process_msg(o, code, data, offset, chunk);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) {
 | 
					/* Called from the IO thread. */
 | 
				
			||||||
 | 
					static int source_set_state_in_io_thread_cb(pa_source *s, pa_source_state_t new_state) {
 | 
				
			||||||
 | 
					    struct userdata *u;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pa_assert(s);
 | 
				
			||||||
 | 
					    pa_assert_se(u = s->userdata);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    switch (new_state) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        case PA_SOURCE_SUSPENDED:
 | 
					        case PA_SOURCE_SUSPENDED:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -479,11 +494,8 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
 | 
				
			||||||
            ;
 | 
					            ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    return 0;
 | 
				
			||||||
 | 
					 | 
				
			||||||
    return pa_source_process_msg(o, code, data, offset, chunk);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void sink_set_volume(pa_sink *s) {
 | 
					static void sink_set_volume(pa_sink *s) {
 | 
				
			||||||
| 
						 | 
					@ -960,6 +972,7 @@ int pa__init(pa_module *m) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        u->source->userdata = u;
 | 
					        u->source->userdata = u;
 | 
				
			||||||
        u->source->parent.process_msg = source_process_msg;
 | 
					        u->source->parent.process_msg = source_process_msg;
 | 
				
			||||||
 | 
					        u->source->set_state_in_io_thread = source_set_state_in_io_thread_cb;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
 | 
					        pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
 | 
				
			||||||
        pa_source_set_rtpoll(u->source, u->rtpoll);
 | 
					        pa_source_set_rtpoll(u->source, u->rtpoll);
 | 
				
			||||||
| 
						 | 
					@ -1003,6 +1016,7 @@ int pa__init(pa_module *m) {
 | 
				
			||||||
        pa_assert(u->sink);
 | 
					        pa_assert(u->sink);
 | 
				
			||||||
        u->sink->userdata = u;
 | 
					        u->sink->userdata = u;
 | 
				
			||||||
        u->sink->parent.process_msg = sink_process_msg;
 | 
					        u->sink->parent.process_msg = sink_process_msg;
 | 
				
			||||||
 | 
					        u->sink->set_state_in_io_thread = sink_set_state_in_io_thread_cb;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
 | 
					        pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
 | 
				
			||||||
        pa_sink_set_rtpoll(u->sink, u->rtpoll);
 | 
					        pa_sink_set_rtpoll(u->sink, u->rtpoll);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -429,11 +429,21 @@ static int sink_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t of
 | 
				
			||||||
            *((int64_t*) data) = remote_latency;
 | 
					            *((int64_t*) data) = remote_latency;
 | 
				
			||||||
            return 0;
 | 
					            return 0;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        case PA_SINK_MESSAGE_SET_STATE:
 | 
					    }
 | 
				
			||||||
            if (!u->stream || pa_stream_get_state(u->stream) != PA_STREAM_READY)
 | 
					    return pa_sink_process_msg(o, code, data, offset, chunk);
 | 
				
			||||||
                break;
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
 | 
					/* Called from the IO thread. */
 | 
				
			||||||
 | 
					static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state) {
 | 
				
			||||||
 | 
					    struct userdata *u;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pa_assert(s);
 | 
				
			||||||
 | 
					    pa_assert_se(u = s->userdata);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!u->stream || pa_stream_get_state(u->stream) != PA_STREAM_READY)
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    switch (new_state) {
 | 
				
			||||||
        case PA_SINK_SUSPENDED: {
 | 
					        case PA_SINK_SUSPENDED: {
 | 
				
			||||||
            cork_stream(u, true);
 | 
					            cork_stream(u, true);
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
| 
						 | 
					@ -448,9 +458,8 @@ static int sink_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t of
 | 
				
			||||||
        case PA_SINK_UNLINKED:
 | 
					        case PA_SINK_UNLINKED:
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
            break;
 | 
					
 | 
				
			||||||
    }
 | 
					    return 0;
 | 
				
			||||||
    return pa_sink_process_msg(o, code, data, offset, chunk);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int pa__init(pa_module *m) {
 | 
					int pa__init(pa_module *m) {
 | 
				
			||||||
| 
						 | 
					@ -545,6 +554,7 @@ int pa__init(pa_module *m) {
 | 
				
			||||||
    pa_sink_new_data_done(&sink_data);
 | 
					    pa_sink_new_data_done(&sink_data);
 | 
				
			||||||
    u->sink->userdata = u;
 | 
					    u->sink->userdata = u;
 | 
				
			||||||
    u->sink->parent.process_msg = sink_process_msg_cb;
 | 
					    u->sink->parent.process_msg = sink_process_msg_cb;
 | 
				
			||||||
 | 
					    u->sink->set_state_in_io_thread = sink_set_state_in_io_thread_cb;
 | 
				
			||||||
    u->sink->update_requested_latency = sink_update_requested_latency_cb;
 | 
					    u->sink->update_requested_latency = sink_update_requested_latency_cb;
 | 
				
			||||||
    pa_sink_set_latency_range(u->sink, 0, MAX_LATENCY_USEC);
 | 
					    pa_sink_set_latency_range(u->sink, 0, MAX_LATENCY_USEC);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -428,11 +428,21 @@ static int source_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return 0;
 | 
					            return 0;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        case PA_SOURCE_MESSAGE_SET_STATE:
 | 
					    }
 | 
				
			||||||
            if (!u->stream || pa_stream_get_state(u->stream) != PA_STREAM_READY)
 | 
					    return pa_source_process_msg(o, code, data, offset, chunk);
 | 
				
			||||||
                break;
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) {
 | 
					/* Called from the IO thread. */
 | 
				
			||||||
 | 
					static int source_set_state_in_io_thread_cb(pa_source *s, pa_source_state_t new_state) {
 | 
				
			||||||
 | 
					    struct userdata *u;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pa_assert(s);
 | 
				
			||||||
 | 
					    pa_assert_se(u = s->userdata);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!u->stream || pa_stream_get_state(u->stream) != PA_STREAM_READY)
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    switch (new_state) {
 | 
				
			||||||
        case PA_SOURCE_SUSPENDED: {
 | 
					        case PA_SOURCE_SUSPENDED: {
 | 
				
			||||||
            cork_stream(u, true);
 | 
					            cork_stream(u, true);
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
| 
						 | 
					@ -447,9 +457,8 @@ static int source_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t
 | 
				
			||||||
        case PA_SOURCE_UNLINKED:
 | 
					        case PA_SOURCE_UNLINKED:
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
            break;
 | 
					
 | 
				
			||||||
    }
 | 
					    return 0;
 | 
				
			||||||
    return pa_source_process_msg(o, code, data, offset, chunk);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int pa__init(pa_module *m) {
 | 
					int pa__init(pa_module *m) {
 | 
				
			||||||
| 
						 | 
					@ -541,6 +550,7 @@ int pa__init(pa_module *m) {
 | 
				
			||||||
    pa_source_new_data_done(&source_data);
 | 
					    pa_source_new_data_done(&source_data);
 | 
				
			||||||
    u->source->userdata = u;
 | 
					    u->source->userdata = u;
 | 
				
			||||||
    u->source->parent.process_msg = source_process_msg_cb;
 | 
					    u->source->parent.process_msg = source_process_msg_cb;
 | 
				
			||||||
 | 
					    u->source->set_state_in_io_thread = source_set_state_in_io_thread_cb;
 | 
				
			||||||
    u->source->update_requested_latency = source_update_requested_latency_cb;
 | 
					    u->source->update_requested_latency = source_update_requested_latency_cb;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pa_source_set_asyncmsgq(u->source, u->thread_mq->inq);
 | 
					    pa_source_set_asyncmsgq(u->source, u->thread_mq->inq);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -106,18 +106,6 @@ static int sink_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t of
 | 
				
			||||||
                pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->sink_input->sink->sample_spec);
 | 
					                pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->sink_input->sink->sample_spec);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return 0;
 | 
					            return 0;
 | 
				
			||||||
 | 
					 | 
				
			||||||
        case PA_SINK_MESSAGE_SET_STATE: {
 | 
					 | 
				
			||||||
            pa_sink_state_t new_state = (pa_sink_state_t) PA_PTR_TO_UINT(data);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            /* When set to running or idle for the first time, request a rewind
 | 
					 | 
				
			||||||
             * of the master sink to make sure we are heard immediately */
 | 
					 | 
				
			||||||
            if ((new_state == PA_SINK_IDLE || new_state == PA_SINK_RUNNING) && u->sink->thread_info.state == PA_SINK_INIT) {
 | 
					 | 
				
			||||||
                pa_log_debug("Requesting rewind due to state change.");
 | 
					 | 
				
			||||||
                pa_sink_input_request_rewind(u->sink_input, 0, false, true, true);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return pa_sink_process_msg(o, code, data, offset, chunk);
 | 
					    return pa_sink_process_msg(o, code, data, offset, chunk);
 | 
				
			||||||
| 
						 | 
					@ -138,6 +126,23 @@ static int sink_set_state_in_main_thread_cb(pa_sink *s, pa_sink_state_t state, p
 | 
				
			||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Called from the IO thread. */
 | 
				
			||||||
 | 
					static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state) {
 | 
				
			||||||
 | 
					    struct userdata *u;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pa_assert(s);
 | 
				
			||||||
 | 
					    pa_assert_se(u = s->userdata);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* When set to running or idle for the first time, request a rewind
 | 
				
			||||||
 | 
					     * of the master sink to make sure we are heard immediately */
 | 
				
			||||||
 | 
					    if ((new_state == PA_SINK_IDLE || new_state == PA_SINK_RUNNING) && u->sink->thread_info.state == PA_SINK_INIT) {
 | 
				
			||||||
 | 
					        pa_log_debug("Requesting rewind due to state change.");
 | 
				
			||||||
 | 
					        pa_sink_input_request_rewind(u->sink_input, 0, false, true, true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Called from I/O thread context */
 | 
					/* Called from I/O thread context */
 | 
				
			||||||
static void sink_request_rewind_cb(pa_sink *s) {
 | 
					static void sink_request_rewind_cb(pa_sink *s) {
 | 
				
			||||||
    struct userdata *u;
 | 
					    struct userdata *u;
 | 
				
			||||||
| 
						 | 
					@ -556,6 +561,7 @@ int pa__init(pa_module*m) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    u->sink->parent.process_msg = sink_process_msg_cb;
 | 
					    u->sink->parent.process_msg = sink_process_msg_cb;
 | 
				
			||||||
    u->sink->set_state_in_main_thread = sink_set_state_in_main_thread_cb;
 | 
					    u->sink->set_state_in_main_thread = sink_set_state_in_main_thread_cb;
 | 
				
			||||||
 | 
					    u->sink->set_state_in_io_thread = sink_set_state_in_io_thread_cb;
 | 
				
			||||||
    u->sink->update_requested_latency = sink_update_requested_latency_cb;
 | 
					    u->sink->update_requested_latency = sink_update_requested_latency_cb;
 | 
				
			||||||
    u->sink->request_rewind = sink_request_rewind_cb;
 | 
					    u->sink->request_rewind = sink_request_rewind_cb;
 | 
				
			||||||
    pa_sink_set_set_mute_callback(u->sink, sink_set_mute_cb);
 | 
					    pa_sink_set_set_mute_callback(u->sink, sink_set_mute_cb);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -134,18 +134,6 @@ static int sink_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t of
 | 
				
			||||||
                pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->sink_input->sink->sample_spec);
 | 
					                pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->sink_input->sink->sample_spec);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return 0;
 | 
					            return 0;
 | 
				
			||||||
 | 
					 | 
				
			||||||
        case PA_SINK_MESSAGE_SET_STATE: {
 | 
					 | 
				
			||||||
            pa_sink_state_t new_state = (pa_sink_state_t) PA_PTR_TO_UINT(data);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            /* When set to running or idle for the first time, request a rewind
 | 
					 | 
				
			||||||
             * of the master sink to make sure we are heard immediately */
 | 
					 | 
				
			||||||
            if ((new_state == PA_SINK_IDLE || new_state == PA_SINK_RUNNING) && u->sink->thread_info.state == PA_SINK_INIT) {
 | 
					 | 
				
			||||||
                pa_log_debug("Requesting rewind due to state change.");
 | 
					 | 
				
			||||||
                pa_sink_input_request_rewind(u->sink_input, 0, false, true, true);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return pa_sink_process_msg(o, code, data, offset, chunk);
 | 
					    return pa_sink_process_msg(o, code, data, offset, chunk);
 | 
				
			||||||
| 
						 | 
					@ -166,6 +154,23 @@ static int sink_set_state_in_main_thread_cb(pa_sink *s, pa_sink_state_t state, p
 | 
				
			||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Called from the IO thread. */
 | 
				
			||||||
 | 
					static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state) {
 | 
				
			||||||
 | 
					    struct userdata *u;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pa_assert(s);
 | 
				
			||||||
 | 
					    pa_assert_se(u = s->userdata);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* When set to running or idle for the first time, request a rewind
 | 
				
			||||||
 | 
					     * of the master sink to make sure we are heard immediately */
 | 
				
			||||||
 | 
					    if ((new_state == PA_SINK_IDLE || new_state == PA_SINK_RUNNING) && u->sink->thread_info.state == PA_SINK_INIT) {
 | 
				
			||||||
 | 
					        pa_log_debug("Requesting rewind due to state change.");
 | 
				
			||||||
 | 
					        pa_sink_input_request_rewind(u->sink_input, 0, false, true, true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Called from I/O thread context */
 | 
					/* Called from I/O thread context */
 | 
				
			||||||
static void sink_request_rewind_cb(pa_sink *s) {
 | 
					static void sink_request_rewind_cb(pa_sink *s) {
 | 
				
			||||||
    struct userdata *u;
 | 
					    struct userdata *u;
 | 
				
			||||||
| 
						 | 
					@ -730,6 +735,7 @@ int pa__init(pa_module*m) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    u->sink->parent.process_msg = sink_process_msg_cb;
 | 
					    u->sink->parent.process_msg = sink_process_msg_cb;
 | 
				
			||||||
    u->sink->set_state_in_main_thread = sink_set_state_in_main_thread_cb;
 | 
					    u->sink->set_state_in_main_thread = sink_set_state_in_main_thread_cb;
 | 
				
			||||||
 | 
					    u->sink->set_state_in_io_thread = sink_set_state_in_io_thread_cb;
 | 
				
			||||||
    u->sink->update_requested_latency = sink_update_requested_latency_cb;
 | 
					    u->sink->update_requested_latency = sink_update_requested_latency_cb;
 | 
				
			||||||
    u->sink->request_rewind = sink_request_rewind_cb;
 | 
					    u->sink->request_rewind = sink_request_rewind_cb;
 | 
				
			||||||
    pa_sink_set_set_mute_callback(u->sink, sink_set_mute_cb);
 | 
					    pa_sink_set_set_mute_callback(u->sink, sink_set_mute_cb);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -643,8 +643,6 @@ fail:
 | 
				
			||||||
/* Called from IO context */
 | 
					/* Called from IO context */
 | 
				
			||||||
static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
 | 
					static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
 | 
				
			||||||
    struct userdata *u = PA_SINK(o)->userdata;
 | 
					    struct userdata *u = PA_SINK(o)->userdata;
 | 
				
			||||||
    bool do_trigger = false, quick = true;
 | 
					 | 
				
			||||||
    pa_sink_state_t new_state;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    switch (code) {
 | 
					    switch (code) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -662,9 +660,19 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return 0;
 | 
					            return 0;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        case PA_SINK_MESSAGE_SET_STATE:
 | 
					    return pa_sink_process_msg(o, code, data, offset, chunk);
 | 
				
			||||||
            new_state = PA_PTR_TO_UINT(data);
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Called from the IO thread. */
 | 
				
			||||||
 | 
					static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state) {
 | 
				
			||||||
 | 
					    struct userdata *u;
 | 
				
			||||||
 | 
					    bool do_trigger = false;
 | 
				
			||||||
 | 
					    bool quick = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pa_assert(s);
 | 
				
			||||||
 | 
					    pa_assert_se(u = s->userdata);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    switch (new_state) {
 | 
					    switch (new_state) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -711,19 +719,14 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
 | 
				
			||||||
            ;
 | 
					            ;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (do_trigger)
 | 
					    if (do_trigger)
 | 
				
			||||||
        trigger(u, new_state, u->source ? u->source->thread_info.state : PA_SOURCE_INVALID_STATE, quick);
 | 
					        trigger(u, new_state, u->source ? u->source->thread_info.state : PA_SOURCE_INVALID_STATE, quick);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return pa_sink_process_msg(o, code, data, offset, chunk);
 | 
					    return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
 | 
					static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
 | 
				
			||||||
    struct userdata *u = PA_SOURCE(o)->userdata;
 | 
					    struct userdata *u = PA_SOURCE(o)->userdata;
 | 
				
			||||||
    bool do_trigger = false, quick = true;
 | 
					 | 
				
			||||||
    pa_source_state_t new_state;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    switch (code) {
 | 
					    switch (code) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -740,9 +743,19 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
 | 
				
			||||||
            *((int64_t*) data) = (int64_t)r;
 | 
					            *((int64_t*) data) = (int64_t)r;
 | 
				
			||||||
            return 0;
 | 
					            return 0;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        case PA_SOURCE_MESSAGE_SET_STATE:
 | 
					    return pa_source_process_msg(o, code, data, offset, chunk);
 | 
				
			||||||
            new_state = PA_PTR_TO_UINT(data);
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Called from the IO thread. */
 | 
				
			||||||
 | 
					static int source_set_state_in_io_thread_cb(pa_source *s, pa_source_state_t new_state) {
 | 
				
			||||||
 | 
					    struct userdata *u;
 | 
				
			||||||
 | 
					    bool do_trigger = false;
 | 
				
			||||||
 | 
					    bool quick = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pa_assert(s);
 | 
				
			||||||
 | 
					    pa_assert_se(u = s->userdata);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    switch (new_state) {
 | 
					    switch (new_state) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -786,15 +799,12 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
 | 
				
			||||||
        case PA_SOURCE_INIT:
 | 
					        case PA_SOURCE_INIT:
 | 
				
			||||||
        case PA_SOURCE_INVALID_STATE:
 | 
					        case PA_SOURCE_INVALID_STATE:
 | 
				
			||||||
            ;
 | 
					            ;
 | 
				
			||||||
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (do_trigger)
 | 
					    if (do_trigger)
 | 
				
			||||||
        trigger(u, u->sink ? u->sink->thread_info.state : PA_SINK_INVALID_STATE, new_state, quick);
 | 
					        trigger(u, u->sink ? u->sink->thread_info.state : PA_SINK_INVALID_STATE, new_state, quick);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return pa_source_process_msg(o, code, data, offset, chunk);
 | 
					    return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void sink_get_volume(pa_sink *s) {
 | 
					static void sink_get_volume(pa_sink *s) {
 | 
				
			||||||
| 
						 | 
					@ -1334,6 +1344,7 @@ int pa__init(pa_module*m) {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        u->source->parent.process_msg = source_process_msg;
 | 
					        u->source->parent.process_msg = source_process_msg;
 | 
				
			||||||
 | 
					        u->source->set_state_in_io_thread = source_set_state_in_io_thread_cb;
 | 
				
			||||||
        u->source->userdata = u;
 | 
					        u->source->userdata = u;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
 | 
					        pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
 | 
				
			||||||
| 
						 | 
					@ -1403,6 +1414,7 @@ int pa__init(pa_module*m) {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        u->sink->parent.process_msg = sink_process_msg;
 | 
					        u->sink->parent.process_msg = sink_process_msg;
 | 
				
			||||||
 | 
					        u->sink->set_state_in_io_thread = sink_set_state_in_io_thread_cb;
 | 
				
			||||||
        u->sink->userdata = u;
 | 
					        u->sink->userdata = u;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
 | 
					        pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -136,64 +136,6 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
 | 
				
			||||||
    pa_assert(u->raop);
 | 
					    pa_assert(u->raop);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    switch (code) {
 | 
					    switch (code) {
 | 
				
			||||||
        case PA_SINK_MESSAGE_SET_STATE: {
 | 
					 | 
				
			||||||
            switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
 | 
					 | 
				
			||||||
                case PA_SINK_SUSPENDED: {
 | 
					 | 
				
			||||||
                    pa_log_debug("RAOP: SUSPENDED");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    /* Issue a TEARDOWN if we are still connected */
 | 
					 | 
				
			||||||
                    if (pa_raop_client_is_alive(u->raop)) {
 | 
					 | 
				
			||||||
                        pa_raop_client_teardown(u->raop);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    break;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                case PA_SINK_IDLE: {
 | 
					 | 
				
			||||||
                    pa_log_debug("RAOP: IDLE");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    /* Issue a FLUSH if we're comming from running state */
 | 
					 | 
				
			||||||
                    if (u->sink->thread_info.state == PA_SINK_RUNNING) {
 | 
					 | 
				
			||||||
                        pa_rtpoll_set_timer_disabled(u->rtpoll);
 | 
					 | 
				
			||||||
                        pa_raop_client_flush(u->raop);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    break;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                case PA_SINK_RUNNING: {
 | 
					 | 
				
			||||||
                    pa_usec_t now;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    pa_log_debug("RAOP: RUNNING");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    now = pa_rtclock_now();
 | 
					 | 
				
			||||||
                    pa_smoother_reset(u->smoother, now, false);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    if (!pa_raop_client_is_alive(u->raop)) {
 | 
					 | 
				
			||||||
                        /* Connecting will trigger a RECORD and start steaming */
 | 
					 | 
				
			||||||
                        pa_raop_client_announce(u->raop);
 | 
					 | 
				
			||||||
                    } else if (!pa_raop_client_can_stream(u->raop)) {
 | 
					 | 
				
			||||||
                        /* RECORD alredy sent, simply start streaming */
 | 
					 | 
				
			||||||
                        pa_raop_client_stream(u->raop);
 | 
					 | 
				
			||||||
                        pa_rtpoll_set_timer_absolute(u->rtpoll, now);
 | 
					 | 
				
			||||||
                        u->write_count = 0;
 | 
					 | 
				
			||||||
                        u->start = now;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    break;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                case PA_SINK_UNLINKED:
 | 
					 | 
				
			||||||
                case PA_SINK_INIT:
 | 
					 | 
				
			||||||
                case PA_SINK_INVALID_STATE:
 | 
					 | 
				
			||||||
                    break;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        case PA_SINK_MESSAGE_GET_LATENCY: {
 | 
					        case PA_SINK_MESSAGE_GET_LATENCY: {
 | 
				
			||||||
            int64_t r = 0;
 | 
					            int64_t r = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -278,6 +220,68 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
 | 
				
			||||||
    return pa_sink_process_msg(o, code, data, offset, chunk);
 | 
					    return pa_sink_process_msg(o, code, data, offset, chunk);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Called from the IO thread. */
 | 
				
			||||||
 | 
					static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state) {
 | 
				
			||||||
 | 
					    struct userdata *u;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pa_assert(s);
 | 
				
			||||||
 | 
					    pa_assert_se(u = s->userdata);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    switch (new_state) {
 | 
				
			||||||
 | 
					        case PA_SINK_SUSPENDED:
 | 
				
			||||||
 | 
					            pa_log_debug("RAOP: SUSPENDED");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            /* Issue a TEARDOWN if we are still connected */
 | 
				
			||||||
 | 
					            if (pa_raop_client_is_alive(u->raop)) {
 | 
				
			||||||
 | 
					                pa_raop_client_teardown(u->raop);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case PA_SINK_IDLE:
 | 
				
			||||||
 | 
					            pa_log_debug("RAOP: IDLE");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            /* Issue a FLUSH if we're comming from running state */
 | 
				
			||||||
 | 
					            if (u->sink->thread_info.state == PA_SINK_RUNNING) {
 | 
				
			||||||
 | 
					                pa_rtpoll_set_timer_disabled(u->rtpoll);
 | 
				
			||||||
 | 
					                pa_raop_client_flush(u->raop);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case PA_SINK_RUNNING: {
 | 
				
			||||||
 | 
					            pa_usec_t now;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            pa_log_debug("RAOP: RUNNING");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            now = pa_rtclock_now();
 | 
				
			||||||
 | 
					            pa_smoother_reset(u->smoother, now, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (!pa_raop_client_is_alive(u->raop)) {
 | 
				
			||||||
 | 
					                /* Connecting will trigger a RECORD and start steaming */
 | 
				
			||||||
 | 
					                pa_raop_client_announce(u->raop);
 | 
				
			||||||
 | 
					            } else if (!pa_raop_client_can_stream(u->raop)) {
 | 
				
			||||||
 | 
					                /* RECORD alredy sent, simply start streaming */
 | 
				
			||||||
 | 
					                pa_raop_client_stream(u->raop);
 | 
				
			||||||
 | 
					                pa_rtpoll_set_timer_absolute(u->rtpoll, now);
 | 
				
			||||||
 | 
					                u->write_count = 0;
 | 
				
			||||||
 | 
					                u->start = now;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case PA_SINK_UNLINKED:
 | 
				
			||||||
 | 
					        case PA_SINK_INIT:
 | 
				
			||||||
 | 
					        case PA_SINK_INVALID_STATE:
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void sink_set_volume_cb(pa_sink *s) {
 | 
					static void sink_set_volume_cb(pa_sink *s) {
 | 
				
			||||||
    struct userdata *u = s->userdata;
 | 
					    struct userdata *u = s->userdata;
 | 
				
			||||||
    pa_cvolume hw;
 | 
					    pa_cvolume hw;
 | 
				
			||||||
| 
						 | 
					@ -696,6 +700,7 @@ pa_sink* pa_raop_sink_new(pa_module *m, pa_modargs *ma, const char *driver) {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    u->sink->parent.process_msg = sink_process_msg;
 | 
					    u->sink->parent.process_msg = sink_process_msg;
 | 
				
			||||||
 | 
					    u->sink->set_state_in_io_thread = sink_set_state_in_io_thread_cb;
 | 
				
			||||||
    pa_sink_set_set_volume_callback(u->sink, sink_set_volume_cb);
 | 
					    pa_sink_set_set_volume_callback(u->sink, sink_set_volume_cb);
 | 
				
			||||||
    pa_sink_set_set_mute_callback(u->sink, sink_set_mute_cb);
 | 
					    pa_sink_set_set_mute_callback(u->sink, sink_set_mute_cb);
 | 
				
			||||||
    u->sink->userdata = u;
 | 
					    u->sink->userdata = u;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -151,6 +151,7 @@ static void reset_callbacks(pa_sink *s) {
 | 
				
			||||||
    pa_assert(s);
 | 
					    pa_assert(s);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    s->set_state_in_main_thread = NULL;
 | 
					    s->set_state_in_main_thread = NULL;
 | 
				
			||||||
 | 
					    s->set_state_in_io_thread = NULL;
 | 
				
			||||||
    s->get_volume = NULL;
 | 
					    s->get_volume = NULL;
 | 
				
			||||||
    s->set_volume = NULL;
 | 
					    s->set_volume = NULL;
 | 
				
			||||||
    s->write_volume = NULL;
 | 
					    s->write_volume = NULL;
 | 
				
			||||||
| 
						 | 
					@ -2850,6 +2851,13 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
 | 
				
			||||||
                (s->thread_info.state == PA_SINK_SUSPENDED && PA_SINK_IS_OPENED(PA_PTR_TO_UINT(userdata))) ||
 | 
					                (s->thread_info.state == PA_SINK_SUSPENDED && PA_SINK_IS_OPENED(PA_PTR_TO_UINT(userdata))) ||
 | 
				
			||||||
                (PA_SINK_IS_OPENED(s->thread_info.state) && PA_PTR_TO_UINT(userdata) == PA_SINK_SUSPENDED);
 | 
					                (PA_SINK_IS_OPENED(s->thread_info.state) && PA_PTR_TO_UINT(userdata) == PA_SINK_SUSPENDED);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (s->set_state_in_io_thread) {
 | 
				
			||||||
 | 
					                int r;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if ((r = s->set_state_in_io_thread(s, PA_PTR_TO_UINT(userdata))) < 0)
 | 
				
			||||||
 | 
					                    return r;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            s->thread_info.state = PA_PTR_TO_UINT(userdata);
 | 
					            s->thread_info.state = PA_PTR_TO_UINT(userdata);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (s->thread_info.state == PA_SINK_SUSPENDED) {
 | 
					            if (s->thread_info.state == PA_SINK_SUSPENDED) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -124,19 +124,31 @@ struct pa_sink {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool set_mute_in_progress;
 | 
					    bool set_mute_in_progress;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* Called when the main loop requests a state change. Called from
 | 
					    /* Callbacks for doing things when the sink state and/or suspend cause is
 | 
				
			||||||
     * main loop context. If returns -1 the state change will be
 | 
					     * changed. It's fine to set either or both of the callbacks to NULL if the
 | 
				
			||||||
     * inhibited. This will also be called even if only the suspend cause
 | 
					     * implementation doesn't have anything to do on state or suspend cause
 | 
				
			||||||
     * changes.
 | 
					     * changes.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * s->state and s->suspend_cause haven't been updated yet when this is
 | 
					     * set_state_in_main_thread() is called first. The callback is allowed to
 | 
				
			||||||
     * called, so the callback can get the old state through those variables.
 | 
					     * report failure if and only if the sink changes its state from
 | 
				
			||||||
 | 
					     * SUSPENDED to IDLE or RUNNING. (FIXME: It would make sense to allow
 | 
				
			||||||
 | 
					     * failure also when changing state from INIT to IDLE or RUNNING, but
 | 
				
			||||||
 | 
					     * currently that will crash pa_sink_put().) If
 | 
				
			||||||
 | 
					     * set_state_in_main_thread() fails, set_state_in_io_thread() won't be
 | 
				
			||||||
 | 
					     * called.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * If set_state_in_main_thread() is successful, the IO thread will be
 | 
					     * If set_state_in_main_thread() is successful (or not set), then
 | 
				
			||||||
     * notified with the SET_STATE message. The message handler is allowed to
 | 
					     * set_state_in_io_thread() is called. Again, failure is allowed if and
 | 
				
			||||||
     * fail, in which case the old state is restored, and
 | 
					     * only if the sink changes state from SUSPENDED to IDLE or RUNNING. If
 | 
				
			||||||
     * set_state_in_main_thread() is called again. */
 | 
					     * set_state_in_io_thread() fails, then set_state_in_main_thread() is
 | 
				
			||||||
 | 
					     * called again, this time with the state parameter set to SUSPENDED and
 | 
				
			||||||
 | 
					     * the suspend_cause parameter set to 0.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * pa_sink.state, pa_sink.thread_info.state and pa_sink.suspend_cause
 | 
				
			||||||
 | 
					     * are updated only after all the callback calls. In case of failure, the
 | 
				
			||||||
 | 
					     * state is set to SUSPENDED and the suspend cause is set to 0. */
 | 
				
			||||||
    int (*set_state_in_main_thread)(pa_sink *s, pa_sink_state_t state, pa_suspend_cause_t suspend_cause); /* may be NULL */
 | 
					    int (*set_state_in_main_thread)(pa_sink *s, pa_sink_state_t state, pa_suspend_cause_t suspend_cause); /* may be NULL */
 | 
				
			||||||
 | 
					    int (*set_state_in_io_thread)(pa_sink *s, pa_sink_state_t state); /* may be NULL */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* Sink drivers that support hardware volume may set this
 | 
					    /* Sink drivers that support hardware volume may set this
 | 
				
			||||||
     * callback. This is called when the current volume needs to be
 | 
					     * callback. This is called when the current volume needs to be
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -142,6 +142,7 @@ static void reset_callbacks(pa_source *s) {
 | 
				
			||||||
    pa_assert(s);
 | 
					    pa_assert(s);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    s->set_state_in_main_thread = NULL;
 | 
					    s->set_state_in_main_thread = NULL;
 | 
				
			||||||
 | 
					    s->set_state_in_io_thread = NULL;
 | 
				
			||||||
    s->get_volume = NULL;
 | 
					    s->get_volume = NULL;
 | 
				
			||||||
    s->set_volume = NULL;
 | 
					    s->set_volume = NULL;
 | 
				
			||||||
    s->write_volume = NULL;
 | 
					    s->write_volume = NULL;
 | 
				
			||||||
| 
						 | 
					@ -2224,6 +2225,13 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_
 | 
				
			||||||
                (s->thread_info.state == PA_SOURCE_SUSPENDED && PA_SOURCE_IS_OPENED(PA_PTR_TO_UINT(userdata))) ||
 | 
					                (s->thread_info.state == PA_SOURCE_SUSPENDED && PA_SOURCE_IS_OPENED(PA_PTR_TO_UINT(userdata))) ||
 | 
				
			||||||
                (PA_SOURCE_IS_OPENED(s->thread_info.state) && PA_PTR_TO_UINT(userdata) == PA_SOURCE_SUSPENDED);
 | 
					                (PA_SOURCE_IS_OPENED(s->thread_info.state) && PA_PTR_TO_UINT(userdata) == PA_SOURCE_SUSPENDED);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (s->set_state_in_io_thread) {
 | 
				
			||||||
 | 
					                int r;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if ((r = s->set_state_in_io_thread(s, PA_PTR_TO_UINT(userdata))) < 0)
 | 
				
			||||||
 | 
					                    return r;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            s->thread_info.state = PA_PTR_TO_UINT(userdata);
 | 
					            s->thread_info.state = PA_PTR_TO_UINT(userdata);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (suspend_change) {
 | 
					            if (suspend_change) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -125,19 +125,31 @@ struct pa_source {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool set_mute_in_progress;
 | 
					    bool set_mute_in_progress;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* Called when the main loop requests a state change. Called from
 | 
					    /* Callbacks for doing things when the source state and/or suspend cause is
 | 
				
			||||||
     * main loop context. If returns -1 the state change will be
 | 
					     * changed. It's fine to set either or both of the callbacks to NULL if the
 | 
				
			||||||
     * inhibited. This will also be called even if only the suspend cause
 | 
					     * implementation doesn't have anything to do on state or suspend cause
 | 
				
			||||||
     * changes.
 | 
					     * changes.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * s->state and s->suspend_cause haven't been updated yet when this is
 | 
					     * set_state_in_main_thread() is called first. The callback is allowed to
 | 
				
			||||||
     * called, so the callback can get the old state through those variables.
 | 
					     * report failure if and only if the source changes its state from
 | 
				
			||||||
 | 
					     * SUSPENDED to IDLE or RUNNING. (FIXME: It would make sense to allow
 | 
				
			||||||
 | 
					     * failure also when changing state from INIT to IDLE or RUNNING, but
 | 
				
			||||||
 | 
					     * currently that will crash pa_source_put().) If
 | 
				
			||||||
 | 
					     * set_state_in_main_thread() fails, set_state_in_io_thread() won't be
 | 
				
			||||||
 | 
					     * called.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * If set_state_in_main_thread() is successful, the IO thread will be
 | 
					     * If set_state_in_main_thread() is successful (or not set), then
 | 
				
			||||||
     * notified with the SET_STATE message. The message handler is allowed to
 | 
					     * set_state_in_io_thread() is called. Again, failure is allowed if and
 | 
				
			||||||
     * fail, in which case the old state is restored, and
 | 
					     * only if the source changes state from SUSPENDED to IDLE or RUNNING. If
 | 
				
			||||||
     * set_state_in_main_thread() is called again. */
 | 
					     * set_state_in_io_thread() fails, then set_state_in_main_thread() is
 | 
				
			||||||
    int (*set_state_in_main_thread)(pa_source *source, pa_source_state_t state, pa_suspend_cause_t suspend_cause); /* may be NULL */
 | 
					     * called again, this time with the state parameter set to SUSPENDED and
 | 
				
			||||||
 | 
					     * the suspend_cause parameter set to 0.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * pa_source.state, pa_source.thread_info.state and pa_source.suspend_cause
 | 
				
			||||||
 | 
					     * are updated only after all the callback calls. In case of failure, the
 | 
				
			||||||
 | 
					     * state is set to SUSPENDED and the suspend cause is set to 0. */
 | 
				
			||||||
 | 
					    int (*set_state_in_main_thread)(pa_source *s, pa_source_state_t state, pa_suspend_cause_t suspend_cause); /* may be NULL */
 | 
				
			||||||
 | 
					    int (*set_state_in_io_thread)(pa_source *s, pa_source_state_t state); /* may be NULL */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* Called when the volume is queried. Called from main loop
 | 
					    /* Called when the volume is queried. Called from main loop
 | 
				
			||||||
     * context. If this is NULL a PA_SOURCE_MESSAGE_GET_VOLUME message
 | 
					     * context. If this is NULL a PA_SOURCE_MESSAGE_GET_VOLUME message
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue