示例#1
0
static void process_rewind(struct userdata *u, pa_usec_t now) {
    size_t rewind_nbytes, in_buffer;
    pa_usec_t delay;

    pa_assert(u);

    rewind_nbytes = u->sink->thread_info.rewind_nbytes;

    if (!PA_SINK_IS_OPENED(u->sink->thread_info.state) || rewind_nbytes <= 0)
        goto do_nothing;

    pa_log_debug("Requested to rewind %lu bytes.", (unsigned long) rewind_nbytes);

    if (u->timestamp <= now)
        goto do_nothing;

    delay = u->timestamp - now;
    in_buffer = pa_usec_to_bytes(delay, &u->sink->sample_spec);

    if (in_buffer <= 0)
        goto do_nothing;

    if (rewind_nbytes > in_buffer)
        rewind_nbytes = in_buffer;

    pa_sink_process_rewind(u->sink, rewind_nbytes);
    u->timestamp -= pa_bytes_to_usec(rewind_nbytes, &u->sink->sample_spec);

    pa_log_debug("Rewound %lu bytes.", (unsigned long) rewind_nbytes);
    return;

do_nothing:

    pa_sink_process_rewind(u->sink, 0);
}
示例#2
0
static void process_rewind(struct userdata *u) {
    size_t rewind_nbytes;

    pa_assert(u);

    if (!PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
        pa_sink_process_rewind(u->sink, 0);
        return;
    }

    rewind_nbytes = u->sink->thread_info.rewind_nbytes;

    if (rewind_nbytes > 0) {
        pa_log_debug("Requested to rewind %lu bytes.", (unsigned long) rewind_nbytes);
        rewind_nbytes = PA_MIN(u->memchunk.length, rewind_nbytes);
        u->memchunk.length -= rewind_nbytes;
        if (u->memchunk.length <= 0 && u->memchunk.memblock) {
            pa_memblock_unref(u->memchunk.memblock);
            pa_memchunk_reset(&u->memchunk);
        }
        pa_log_debug("Rewound %lu bytes.", (unsigned long) rewind_nbytes);
    }

    pa_sink_process_rewind(u->sink, rewind_nbytes);
}
示例#3
0
/* Called from I/O thread context */
static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
    struct userdata *u;
    size_t amount = 0;

    pa_sink_input_assert_ref(i);
    pa_assert_se(u = i->userdata);

    if (u->sink->thread_info.rewind_nbytes > 0) {
        size_t max_rewrite;

        max_rewrite = nbytes + pa_memblockq_get_length(u->memblockq);
        amount = PA_MIN(u->sink->thread_info.rewind_nbytes, max_rewrite);
        u->sink->thread_info.rewind_nbytes = 0;

        if (amount > 0) {
            unsigned c;

            pa_memblockq_seek(u->memblockq, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE);

            pa_log_debug("Resetting plugin");

            /* Reset the plugin */
            if (u->descriptor->deactivate)
                for (c = 0; c < (u->channels / u->max_ladspaport_count); c++)
                    u->descriptor->deactivate(u->handle[c]);
            if (u->descriptor->activate)
                for (c = 0; c < (u->channels / u->max_ladspaport_count); c++)
                    u->descriptor->activate(u->handle[c]);
        }
    }

    pa_sink_process_rewind(u->sink, amount);
    pa_memblockq_rewind(u->memblockq, nbytes);
}
static void thread_func(void *userdata) {
    struct userdata *u = userdata;

    pa_assert(u);

    pa_log_debug("Thread starting up");

    if (u->core->realtime_scheduling)
        pa_make_realtime(u->core->realtime_priority);

    pa_thread_mq_install(&u->thread_mq);

    for (;;) {
        int ret;

        if (PA_SINK_IS_OPENED(u->sink->thread_info.state))
            if (u->sink->thread_info.rewind_requested)
                pa_sink_process_rewind(u->sink, 0);

        if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
            goto fail;

        if (ret == 0)
            goto finish;
    }

fail:
    /* If this was no regular exit from the loop we have to continue
     * processing messages until we received PA_MESSAGE_SHUTDOWN */
    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);

finish:
    pa_log_debug("Thread shutting down");
}
/* Called from I/O thread context */
static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
    struct userdata *u;
    size_t amount = 0;

    pa_sink_input_assert_ref(i);
    pa_assert_se(u = i->userdata);

    if (u->sink->thread_info.rewind_nbytes > 0) {
        size_t max_rewrite;

        max_rewrite = nbytes * u->sink_fs / u->fs + pa_memblockq_get_length(u->memblockq);
        amount = PA_MIN(u->sink->thread_info.rewind_nbytes * u->sink_fs / u->fs, max_rewrite);
        u->sink->thread_info.rewind_nbytes = 0;

        if (amount > 0) {
            pa_memblockq_seek(u->memblockq, - (int64_t) amount, PA_SEEK_RELATIVE, true);

            /* Reset the input buffer */
            memset(u->input_buffer, 0, u->hrir_samples * u->sink_fs);
            u->input_buffer_offset = 0;
        }
    }

    pa_sink_process_rewind(u->sink, amount);
    pa_memblockq_rewind(u->memblockq, nbytes * u->sink_fs / u->fs);
}
示例#6
0
static void thread_func(void *userdata) {

    struct userdata *u = userdata;
    int ret;
    pa_usec_t now;

    pa_assert(u);

    pa_log_debug("Thread starting up");

    pa_thread_mq_install(&u->thread_mq);

    u->timestamp = pa_rtclock_now();

    for (;;) {

        if (u->sink->thread_info.state == PA_SINK_RUNNING) {

            now = pa_rtclock_now();

            if (u->sink->thread_info.rewind_requested) {
                if (u->sink->thread_info.rewind_nbytes > 0) {
                    process_rewind(u, now);
                } else {
                    pa_sink_process_rewind(u->sink, 0);
                }
            }

            if (u->timestamp <= now) {
                pa_log_debug("thread_func: calling process_render");
                process_render(u, now);
            }

            pa_rtpoll_set_timer_absolute(u->rtpoll, u->timestamp);

        } else {
            pa_rtpoll_set_timer_disabled(u->rtpoll);
        }

        if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) {
            goto fail;
        }

        if (ret == 0) {
            goto finish;
        }
    }

fail:
    /* If this was no regular exit from the loop we have to continue
     * processing messages until we received PA_MESSAGE_SHUTDOWN */
    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core),
                      PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0,
                      NULL, NULL);
    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);

finish:
    pa_log_debug("Thread shutting down");
}
示例#7
0
static void thread_func(void *userdata) {
    struct userdata *u = userdata;

    pa_assert(u);

    pa_log_debug("Thread starting up");

    pa_thread_mq_install(&u->thread_mq);

    for(;;){
        struct pollfd *pollfd;
        int ret;

        pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);

        /* Render some data and write it to the fifo */
        if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {

            if (u->sink->thread_info.rewind_requested)
                pa_sink_process_rewind(u->sink, 0);

            if (pollfd->revents) {
                if (process_render(u) < 0)
                    goto fail;

                pollfd->revents = 0;
            }
        }

        /* Hmm, nothing to do. Let's sleep */

        pollfd->events = (short) (u->sink->thread_info.state == PA_SINK_RUNNING ? POLLOUT : 0);

        if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
            goto fail;

        if (ret == 0)
            goto finish;

        pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);

        if (pollfd->revents & ~POLLOUT) {
            pa_log("FIFO shutdown.");
            goto fail;
        }
    }

fail:
    /* If this was no regular exit from the loop we have to continue
     * processing messages until we received PA_MESSAGE_SHUTDOWN */
    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
    pa_log_debug("Shutting down Xen...");
    xen_cleanup();
finish:
    pa_log_debug("Thread shutting down");
}
/* Called from I/O thread context */
static void sink_request_rewind_cb(pa_sink *s) {
    struct userdata *u;

    pa_sink_assert_ref(s);
    pa_assert_se(u = s->userdata);

    /* Do nothing */
    pa_sink_process_rewind(u->sink, 0);

}
示例#9
0
/* Called from I/O thread context */
static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
    struct userdata *u;
    float *src, *dst;
    size_t fs;
    unsigned n, h, c;
    pa_memchunk tchunk;

    pa_sink_input_assert_ref(i);
    pa_assert(chunk);
    pa_assert_se(u = i->userdata);

    /* Hmm, process any rewind request that might be queued up */
    pa_sink_process_rewind(u->sink, 0);

    while (pa_memblockq_peek(u->memblockq, &tchunk) < 0) {
        pa_memchunk nchunk;

        pa_sink_render(u->sink, nbytes, &nchunk);
        pa_memblockq_push(u->memblockq, &nchunk);
        pa_memblock_unref(nchunk.memblock);
    }

    tchunk.length = PA_MIN(nbytes, tchunk.length);
    pa_assert(tchunk.length > 0);

    fs = pa_frame_size(&i->sample_spec);
    n = (unsigned) (PA_MIN(tchunk.length, u->block_size) / fs);

    pa_assert(n > 0);

    chunk->index = 0;
    chunk->length = n*fs;
    chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length);

    pa_memblockq_drop(u->memblockq, chunk->length);

    src = (float*) ((uint8_t*) pa_memblock_acquire(tchunk.memblock) + tchunk.index);
    dst = (float*) pa_memblock_acquire(chunk->memblock);

    for (h = 0; h < (u->channels / u->max_ladspaport_count); h++) {
        for (c = 0; c < u->input_count; c++)
            pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input[c], sizeof(float), src+ h*u->max_ladspaport_count + c, u->channels*sizeof(float), n);
        u->descriptor->run(u->handle[h], n);
        for (c = 0; c < u->output_count; c++)
            pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst + h*u->max_ladspaport_count + c, u->channels*sizeof(float), u->output[c], sizeof(float), n);
    }

    pa_memblock_release(tchunk.memblock);
    pa_memblock_release(chunk->memblock);

    pa_memblock_unref(tchunk.memblock);

    return 0;
}
示例#10
0
static void thread_func(void *userdata) {
    struct userdata *u = userdata;

    pa_assert(u);
    pa_assert(u->sink || u->source);

    pa_log_debug("Thread starting up");

    if (u->core->realtime_scheduling)
        pa_make_realtime(u->core->realtime_priority);

    pa_thread_mq_install(&u->thread_mq);

    for (;;) {
        int ret;
        pa_bool_t need_timer = FALSE;

        if (u->sink) {
            if (PA_UNLIKELY(u->sink->thread_info.rewind_requested))
                pa_sink_process_rewind(u->sink, 0);

            if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
                do_write(u);
                need_timer = TRUE;
            }
        }

        if (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
            do_read(u);
            need_timer = TRUE;
        }

        if (need_timer)
            pa_rtpoll_set_timer_relative(u->rtpoll, u->poll_timeout);
        else
            pa_rtpoll_set_timer_disabled(u->rtpoll);

        /* Hmm, nothing to do. Let's sleep */
        if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
            goto fail;

        if (ret == 0)
            goto finish;
    }

fail:
    /* If this was no regular exit from the loop we have to continue
     * processing messages until we received PA_MESSAGE_SHUTDOWN */
    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);

finish:
    pa_log_debug("Thread shutting down");
}
示例#11
0
static void process_rewind(struct context *context, pa_usec_t now)
{
	size_t rewind_nbytes, in_buffer;
	pa_usec_t delay;

	pa_assert(context);

	/* Figure out how much we shall rewind and reset the counter */
	rewind_nbytes = context->sink->thread_info.rewind_nbytes;
	context->sink->thread_info.rewind_nbytes = 0;

	if (rewind_nbytes <= 0)
		goto DO_NOTHING;

	pa_log_debug("Requested to rewind %lu bytes.",
		(unsigned long) rewind_nbytes);

	if (context->timestamp <= now)
		goto DO_NOTHING;

	delay = context->timestamp - now;
	in_buffer = pa_usec_to_bytes(delay, &context->sink->sample_spec);

	if (in_buffer <= 0)
		goto DO_NOTHING;

	if (rewind_nbytes > in_buffer)
		rewind_nbytes = in_buffer;

	pa_sink_process_rewind(context->sink, rewind_nbytes);
	context->timestamp -= pa_bytes_to_usec(rewind_nbytes, &context->sink->sample_spec);
	context->skip_bytes += rewind_nbytes;

	pa_log_debug("Rewound %lu bytes.", (unsigned long) rewind_nbytes);

	return;

DO_NOTHING:
	pa_sink_process_rewind(context->sink, 0);
}
示例#12
0
/* Called from I/O thread context */
static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
    struct userdata *u;

    pa_sink_input_assert_ref(i);
    pa_assert(chunk);
    pa_assert_se(u = i->userdata);

    /* Hmm, process any rewind request that might be queued up */
    pa_sink_process_rewind(u->sink, 0);

    pa_sink_render(u->sink, nbytes, chunk);
    return 0;
}
示例#13
0
/* Called from I/O thread context */
static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
    size_t amount = 0;
    struct userdata *u;

    pa_sink_input_assert_ref(i);
    pa_assert_se(u = i->userdata);

    if (u->sink->thread_info.rewind_nbytes > 0) {
        amount = PA_MIN(u->sink->thread_info.rewind_nbytes, nbytes);
        u->sink->thread_info.rewind_nbytes = 0;
    }

    pa_sink_process_rewind(u->sink, amount);
}
示例#14
0
static void process_rewind(struct userdata *u, pa_usec_t now) {
    size_t rewind_nbytes, in_buffer;
    pa_usec_t delay;

    pa_assert(u);

    /* Figure out how much we shall rewind and reset the counter */
    rewind_nbytes = u->sink->thread_info.rewind_nbytes;
    u->sink->thread_info.rewind_nbytes = 0;

    pa_assert(rewind_nbytes > 0);
    pa_log_debug("Requested to rewind %lu bytes.",
                 (unsigned long) rewind_nbytes);

    if (u->timestamp <= now)
        goto do_nothing;

    delay = u->timestamp - now;
    in_buffer = pa_usec_to_bytes(delay, &u->sink->sample_spec);

    if (in_buffer <= 0)
        goto do_nothing;

    if (rewind_nbytes > in_buffer)
        rewind_nbytes = in_buffer;

    pa_sink_process_rewind(u->sink, rewind_nbytes);
    u->timestamp -= pa_bytes_to_usec(rewind_nbytes, &u->sink->sample_spec);
    u->skip_bytes += rewind_nbytes;

    pa_log_debug("Rewound %lu bytes.", (unsigned long) rewind_nbytes);
    return;

do_nothing:

    pa_sink_process_rewind(u->sink, 0);
}
示例#15
0
/* Called from I/O thread context */
static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
    size_t amount = 0;
    struct userdata *u;

    pa_sink_input_assert_ref(i);
    pa_assert_se(u = i->userdata);

    /* If the sink is not yet linked, there is nothing to rewind */
    if (!PA_SINK_IS_LINKED(u->sink->thread_info.state))
        return;

    if (u->sink->thread_info.rewind_nbytes > 0) {
        amount = PA_MIN(u->sink->thread_info.rewind_nbytes, nbytes);
        u->sink->thread_info.rewind_nbytes = 0;
    }

    pa_sink_process_rewind(u->sink, amount);
}
示例#16
0
/* Called from I/O thread context */
static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
    struct userdata *u;
    size_t amount = 0;

    pa_sink_input_assert_ref(i);
    pa_assert_se(u = i->userdata);

    if (u->sink->thread_info.rewind_nbytes > 0) {
        size_t max_rewrite;

        max_rewrite = nbytes + pa_memblockq_get_length(u->memblockq);
        amount = PA_MIN(u->sink->thread_info.rewind_nbytes, max_rewrite);
        u->sink->thread_info.rewind_nbytes = 0;

        if (amount > 0) {
            pa_memblockq_seek(u->memblockq, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE);

            /* (5) PUT YOUR CODE HERE TO RESET YOUR FILTER  */
        }
    }

    pa_sink_process_rewind(u->sink, amount);
    pa_memblockq_rewind(u->memblockq, nbytes);
}
示例#17
0
文件: module-oss.c 项目: Thread974/pa
static void thread_func(void *userdata) {
    struct userdata *u = userdata;
    int write_type = 0, read_type = 0;
    short revents = 0;

    pa_assert(u);

    pa_log_debug("Thread starting up");

    if (u->core->realtime_scheduling)
        pa_make_realtime(u->core->realtime_priority);

    pa_thread_mq_install(&u->thread_mq);

    for (;;) {
        int ret;

/*        pa_log("loop");    */

        if (PA_UNLIKELY(u->sink && u->sink->thread_info.rewind_requested))
            pa_sink_process_rewind(u->sink, 0);

        /* Render some data and write it to the dsp */

        if (u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state) && ((revents & POLLOUT) || u->use_mmap || u->use_getospace)) {

            if (u->use_mmap) {

                if ((ret = mmap_write(u)) < 0)
                    goto fail;

                revents &= ~POLLOUT;

                if (ret > 0)
                    continue;

            } else {
                ssize_t l;
                pa_bool_t loop = FALSE, work_done = FALSE;

                l = (ssize_t) u->out_fragment_size;

                if (u->use_getospace) {
                    audio_buf_info info;

                    if (ioctl(u->fd, SNDCTL_DSP_GETOSPACE, &info) < 0) {
                        pa_log_info("Device doesn't support SNDCTL_DSP_GETOSPACE: %s", pa_cstrerror(errno));
                        u->use_getospace = FALSE;
                    } else {
                        l = info.bytes;

                        /* We loop only if GETOSPACE worked and we
                         * actually *know* that we can write more than
                         * one fragment at a time */
                        loop = TRUE;
                    }
                }

                /* Round down to multiples of the fragment size,
                 * because OSS needs that (at least some versions
                 * do) */
                l = (l/(ssize_t) u->out_fragment_size) * (ssize_t) u->out_fragment_size;

                /* Hmm, so poll() signalled us that we can read
                 * something, but GETOSPACE told us there was nothing?
                 * Hmm, make the best of it, try to read some data, to
                 * avoid spinning forever. */
                if (l <= 0 && (revents & POLLOUT)) {
                    l = (ssize_t) u->out_fragment_size;
                    loop = FALSE;
                }

                while (l > 0) {
                    void *p;
                    ssize_t t;

                    if (u->memchunk.length <= 0)
                        pa_sink_render(u->sink, (size_t) l, &u->memchunk);

                    pa_assert(u->memchunk.length > 0);

                    p = pa_memblock_acquire(u->memchunk.memblock);
                    t = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, &write_type);
                    pa_memblock_release(u->memchunk.memblock);

/*                     pa_log("wrote %i bytes of %u", t, l); */

                    pa_assert(t != 0);

                    if (t < 0) {

                        if (errno == EINTR)
                            continue;

                        else if (errno == EAGAIN) {
                            pa_log_debug("EAGAIN");

                            revents &= ~POLLOUT;
                            break;

                        } else {
                            pa_log("Failed to write data to DSP: %s", pa_cstrerror(errno));
                            goto fail;
                        }

                    } else {

                        u->memchunk.index += (size_t) t;
                        u->memchunk.length -= (size_t) t;

                        if (u->memchunk.length <= 0) {
                            pa_memblock_unref(u->memchunk.memblock);
                            pa_memchunk_reset(&u->memchunk);
                        }

                        l -= t;

                        revents &= ~POLLOUT;
                        work_done = TRUE;
                    }

                    if (!loop)
                        break;
                }

                if (work_done)
                    continue;
            }
        }

        /* Try to read some data and pass it on to the source driver. */

        if (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state) && ((revents & POLLIN) || u->use_mmap || u->use_getispace)) {

            if (u->use_mmap) {

                if ((ret = mmap_read(u)) < 0)
                    goto fail;

                revents &= ~POLLIN;

                if (ret > 0)
                    continue;

            } else {

                void *p;
                ssize_t l;
                pa_memchunk memchunk;
                pa_bool_t loop = FALSE, work_done = FALSE;

                l = (ssize_t) u->in_fragment_size;

                if (u->use_getispace) {
                    audio_buf_info info;

                    if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) < 0) {
                        pa_log_info("Device doesn't support SNDCTL_DSP_GETISPACE: %s", pa_cstrerror(errno));
                        u->use_getispace = FALSE;
                    } else {
                        l = info.bytes;
                        loop = TRUE;
                    }
                }

                l = (l/(ssize_t) u->in_fragment_size) * (ssize_t) u->in_fragment_size;

                if (l <= 0 && (revents & POLLIN)) {
                    l = (ssize_t) u->in_fragment_size;
                    loop = FALSE;
                }

                while (l > 0) {
                    ssize_t t;
                    size_t k;

                    pa_assert(l > 0);

                    memchunk.memblock = pa_memblock_new(u->core->mempool, (size_t) -1);

                    k = pa_memblock_get_length(memchunk.memblock);

                    if (k > (size_t) l)
                        k = (size_t) l;

                    k = (k/u->frame_size)*u->frame_size;

                    p = pa_memblock_acquire(memchunk.memblock);
                    t = pa_read(u->fd, p, k, &read_type);
                    pa_memblock_release(memchunk.memblock);

                    pa_assert(t != 0); /* EOF cannot happen */

/*                     pa_log("read %i bytes of %u", t, l); */

                    if (t < 0) {
                        pa_memblock_unref(memchunk.memblock);

                        if (errno == EINTR)
                            continue;

                        else if (errno == EAGAIN) {
                            pa_log_debug("EAGAIN");

                            revents &= ~POLLIN;
                            break;

                        } else {
                            pa_log("Failed to read data from DSP: %s", pa_cstrerror(errno));
                            goto fail;
                        }

                    } else {
                        memchunk.index = 0;
                        memchunk.length = (size_t) t;

                        pa_source_post(u->source, &memchunk);
                        pa_memblock_unref(memchunk.memblock);

                        l -= t;

                        revents &= ~POLLIN;
                        work_done = TRUE;
                    }

                    if (!loop)
                        break;
                }

                if (work_done)
                    continue;
            }
        }

/*         pa_log("loop2 revents=%i", revents); */

        if (u->rtpoll_item) {
            struct pollfd *pollfd;

            pa_assert(u->fd >= 0);

            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
            pollfd->events = (short)
                (((u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state)) ? POLLIN : 0) |
                 ((u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state)) ? POLLOUT : 0));
        }

        /* Hmm, nothing to do. Let's sleep */
        if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
            goto fail;

        if (ret == 0)
            goto finish;

        if (u->rtpoll_item) {
            struct pollfd *pollfd;

            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);

            if (pollfd->revents & ~(POLLOUT|POLLIN)) {
                pa_log("DSP shutdown.");
                goto fail;
            }

            revents = pollfd->revents;
        } else
            revents = 0;
    }

fail:
    /* If this was no regular exit from the loop we have to continue
     * processing messages until we received PA_MESSAGE_SHUTDOWN */
    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);

finish:
    pa_log_debug("Thread shutting down");
}
示例#18
0
static void process_rewind(struct example_sink_userdata *u)
{
    pa_assert(u);

    pa_sink_process_rewind(u->sink, 0);
}
static void thread_func(void *userdata)
{
    struct userdata *u = userdata;
    char buf[2048]; // max ring buffer size

    pa_assert(u);

    pa_log_debug("Thread starting up");

    pa_thread_mq_install(&u->thread_mq);
    for (;;) {
        struct pollfd *play_pollfd;
        struct pollfd *rec_pollfd;
        int ret;

        play_pollfd = pa_rtpoll_item_get_pollfd(u->play_rtpoll_item, NULL);
        rec_pollfd = pa_rtpoll_item_get_pollfd(u->rec_rtpoll_item, NULL);

        if (play_pollfd->revents & POLLIN) {
            if (libvchan_wait(u->play_ctrl) < 0)
                goto fail;
            play_pollfd->revents = 0;
        }

        if (rec_pollfd->revents & POLLIN) {
            if (libvchan_wait(u->rec_ctrl) < 0)
                goto fail;
            rec_pollfd->revents = 0;
        }

        /* Render some data and write it to the fifo */
        if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {

            if (u->sink->thread_info.rewind_requested)
                pa_sink_process_rewind(u->sink, 0);

            if (libvchan_buffer_space(u->play_ctrl)) {
                if (process_sink_render(u) < 0)
                    goto fail;
            }
        }

        if (u->source->thread_info.state == PA_SOURCE_RUNNING) {
            while (libvchan_data_ready(u->rec_ctrl)) {
                if (process_source_data(u) < 0)
                    goto fail;
            }
        } else {
            /* discard the data */
            if (libvchan_data_ready(u->rec_ctrl))
                if (libvchan_read(u->rec_ctrl, buf, sizeof(buf)) < 0)
                    goto fail;
        }

        /* Hmm, nothing to do. Let's sleep */
        play_pollfd->events = POLLIN;
        rec_pollfd->events = POLLIN;

#if PA_CHECK_VERSION(6,0,0)
        if ((ret = pa_rtpoll_run(u->rtpoll)) < 0)
#else
        if ((ret = pa_rtpoll_run(u->rtpoll, true)) < 0)
#endif
            goto fail;

        if (ret == 0)
            goto finish;
    }

fail:
    /* If this was no regular exit from the loop we have to continue
     * processing messages until we received PA_MESSAGE_SHUTDOWN */
    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core),
                      PA_CORE_MESSAGE_UNLOAD_MODULE, u->module,
                      0, NULL, NULL);
    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);

finish:
    pa_log_debug("Thread shutting down");
}
static void thread_func(void *userdata) {
    struct userdata *u = userdata;
    pa_proplist *proplist;
    pa_assert(u);

    pa_log_debug("Thread starting up");
    pa_thread_mq_install(u->thread_mq);

    proplist = tunnel_new_proplist(u);
    u->context = pa_context_new_with_proplist(u->thread_mainloop_api,
                                              "PulseAudio",
                                              proplist);
    pa_proplist_free(proplist);

    if (!u->context) {
        pa_log("Failed to create libpulse context");
        goto fail;
    }

    if (u->cookie_file && pa_context_load_cookie_from_file(u->context, u->cookie_file) != 0) {
        pa_log_error("Can not load cookie file!");
        goto fail;
    }

    pa_context_set_state_callback(u->context, context_state_cb, u);
    if (pa_context_connect(u->context,
                           u->remote_server,
                           PA_CONTEXT_NOAUTOSPAWN,
                           NULL) < 0) {
        pa_log("Failed to connect libpulse context");
        goto fail;
    }

    for (;;) {
        int ret;

        if (pa_mainloop_iterate(u->thread_mainloop, 1, &ret) < 0) {
            if (ret == 0)
                goto finish;
            else
                goto fail;
        }

        if (PA_UNLIKELY(u->sink->thread_info.rewind_requested))
            pa_sink_process_rewind(u->sink, 0);

        if (u->connected &&
                pa_stream_get_state(u->stream) == PA_STREAM_READY &&
                PA_SINK_IS_LINKED(u->sink->thread_info.state)) {
            size_t writable;

            writable = pa_stream_writable_size(u->stream);
            if (writable > 0) {
            
                if(u->transcode.encoding != -1) {
                         pa_memchunk memchunk;
                         const void *p;
                         size_t nbBytes;
                         unsigned char *cbits;
                         
                         pa_sink_render_full(u->sink, u->transcode.frame_size*u->transcode.channels*u->transcode.sample_size, &memchunk);
                           
                         pa_assert(memchunk.length > 0);
                         pa_assert(memchunk.length >=  u->transcode.frame_size*u->transcode.channels);
                         
                         
                         pa_log_debug("received memchunk length: %zu bytes", memchunk.length );
                         /* we have new data to write */
                         p = pa_memblock_acquire(memchunk.memblock);
   
                         nbBytes = pa_transcode_encode(&u->transcode, (uint8_t*) p + memchunk.index, &cbits);
                         pa_log_debug("encoded length: %zu bytes", nbBytes);
                                                        
                         /* TODO: Use pa_stream_begin_write() to reduce copying. */
                         ret = pa_stream_write_compressed(u->stream,
                                               (uint8_t*) cbits,
                                               nbBytes,
                                               NULL,     /**< A cleanup routine for the data or NULL to request an internal copy */
                                               0,        /** offset */
                                              PA_SEEK_RELATIVE, u->transcode.frame_size*u->transcode.channels*u->transcode.sample_size);
                         pa_memblock_release(memchunk.memblock);
                         pa_memblock_unref(memchunk.memblock);
                         if(nbBytes > 0) free(cbits);
                         
                         if (ret != 0) {
                             pa_log_error("Could not write data into the stream ... ret = %i", ret);
                             u->thread_mainloop_api->quit(u->thread_mainloop_api, TUNNEL_THREAD_FAILED_MAINLOOP);
                         }
                }
                else { 
                         pa_memchunk memchunk;
                         const void *p;

                         pa_sink_render_full(u->sink, writable, &memchunk);

                         pa_assert(memchunk.length > 0);

                         /* we have new data to write */
                         p = pa_memblock_acquire(memchunk.memblock);
                         /* TODO: Use pa_stream_begin_write() to reduce copying. */
                         ret = pa_stream_write(u->stream,
                                               (uint8_t*) p + memchunk.index,
                                               memchunk.length,
                                               NULL,     /**< A cleanup routine for the data or NULL to request an internal copy */
                                               0,        /** offset */
                                               PA_SEEK_RELATIVE);
                         pa_memblock_release(memchunk.memblock);
                         pa_memblock_unref(memchunk.memblock);

                         if (ret != 0) {
                             pa_log_error("Could not write data into the stream ... ret = %i", ret);
                             u->thread_mainloop_api->quit(u->thread_mainloop_api, TUNNEL_THREAD_FAILED_MAINLOOP);
                         }
                
                }

            }
        }
    }
fail:
    pa_asyncmsgq_post(u->thread_mq->outq, PA_MSGOBJECT(u->module->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
    pa_asyncmsgq_wait_for(u->thread_mq->inq, PA_MESSAGE_SHUTDOWN);

finish:
    if (u->stream) {
        pa_stream_disconnect(u->stream);
        pa_stream_unref(u->stream);
        u->stream = NULL;
    }

    if (u->context) {
        pa_context_disconnect(u->context);
        pa_context_unref(u->context);
        u->context = NULL;
    }

    pa_log_debug("Thread shutting down");
}
示例#21
0
/* Called from I/O thread context */
static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
    struct userdata *u;
    float *src, *dst;
    size_t fs;
    unsigned n, c;
    pa_memchunk tchunk;
    pa_usec_t current_latency PA_GCC_UNUSED;

    pa_sink_input_assert_ref(i);
    pa_assert(chunk);
    pa_assert_se(u = i->userdata);

    /* Hmm, process any rewind request that might be queued up */
    pa_sink_process_rewind(u->sink, 0);

    /* (1) IF YOU NEED A FIXED BLOCK SIZE USE
     * pa_memblockq_peek_fixed_size() HERE INSTEAD. NOTE THAT FILTERS
     * WHICH CAN DEAL WITH DYNAMIC BLOCK SIZES ARE HIGHLY
     * PREFERRED. */
    while (pa_memblockq_peek(u->memblockq, &tchunk) < 0) {
        pa_memchunk nchunk;

        pa_sink_render(u->sink, nbytes, &nchunk);
        pa_memblockq_push(u->memblockq, &nchunk);
        pa_memblock_unref(nchunk.memblock);
    }

    /* (2) IF YOU NEED A FIXED BLOCK SIZE, THIS NEXT LINE IS NOT
     * NECESSARY */
    tchunk.length = PA_MIN(nbytes, tchunk.length);
    pa_assert(tchunk.length > 0);

    fs = pa_frame_size(&i->sample_spec);
    n = (unsigned) (tchunk.length / fs);

    pa_assert(n > 0);

    chunk->index = 0;
    chunk->length = n*fs;
    chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length);

    pa_memblockq_drop(u->memblockq, chunk->length);

    src = pa_memblock_acquire_chunk(&tchunk);
    dst = pa_memblock_acquire(chunk->memblock);

    /* (3) PUT YOUR CODE HERE TO DO SOMETHING WITH THE DATA */

    /* As an example, copy input to output */
    for (c = 0; c < u->channels; c++) {
        pa_sample_clamp(PA_SAMPLE_FLOAT32NE,
                        dst+c, u->channels * sizeof(float),
                        src+c, u->channels * sizeof(float),
                        n);
    }

    pa_memblock_release(tchunk.memblock);
    pa_memblock_release(chunk->memblock);

    pa_memblock_unref(tchunk.memblock);

    /* (4) IF YOU NEED THE LATENCY FOR SOMETHING ACQUIRE IT LIKE THIS: */
    current_latency =
        /* Get the latency of the master sink */
        pa_sink_get_latency_within_thread(i->sink) +

        /* Add the latency internal to our sink input on top */
        pa_bytes_to_usec(pa_memblockq_get_length(i->thread_info.render_memblockq), &i->sink->sample_spec);

    return 0;
}
示例#22
0
static void thread_func(void *userdata) {
    struct userdata *u = userdata;
    int write_type = 0;
    pa_memchunk silence;
    uint32_t silence_overhead = 0;
    double silence_ratio = 0;

    pa_assert(u);

    pa_log_debug("Thread starting up");

    pa_thread_mq_install(&u->thread_mq);

    pa_smoother_set_time_offset(u->smoother, pa_rtclock_now());

    /* Create a chunk of memory that is our encoded silence sample. */
    pa_memchunk_reset(&silence);

    for (;;) {
        int ret;

        if (PA_UNLIKELY(u->sink->thread_info.rewind_requested))
            pa_sink_process_rewind(u->sink, 0);

        if (u->rtpoll_item) {
            struct pollfd *pollfd;
            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);

            /* Render some data and write it to the fifo */
            if (/*PA_SINK_IS_OPENED(u->sink->thread_info.state) && */pollfd->revents) {
                pa_usec_t usec;
                int64_t n;
                void *p;

                if (!silence.memblock) {
                    pa_memchunk silence_tmp;

                    pa_memchunk_reset(&silence_tmp);
                    silence_tmp.memblock = pa_memblock_new(u->core->mempool, 4096);
                    silence_tmp.length = 4096;
                    p = pa_memblock_acquire(silence_tmp.memblock);
                    memset(p, 0, 4096);
                    pa_memblock_release(silence_tmp.memblock);
                    pa_raop_client_encode_sample(u->raop, &silence_tmp, &silence);
                    pa_assert(0 == silence_tmp.length);
                    silence_overhead = silence_tmp.length - 4096;
                    silence_ratio = silence_tmp.length / 4096;
                    pa_memblock_unref(silence_tmp.memblock);
                }

                for (;;) {
                    ssize_t l;

                    if (u->encoded_memchunk.length <= 0) {
                        if (u->encoded_memchunk.memblock)
                            pa_memblock_unref(u->encoded_memchunk.memblock);
                        if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
                            size_t rl;

                            /* We render real data */
                            if (u->raw_memchunk.length <= 0) {
                                if (u->raw_memchunk.memblock)
                                    pa_memblock_unref(u->raw_memchunk.memblock);
                                pa_memchunk_reset(&u->raw_memchunk);

                                /* Grab unencoded data */
                                pa_sink_render(u->sink, u->block_size, &u->raw_memchunk);
                            }
                            pa_assert(u->raw_memchunk.length > 0);

                            /* Encode it */
                            rl = u->raw_memchunk.length;
                            u->encoding_overhead += u->next_encoding_overhead;
                            pa_raop_client_encode_sample(u->raop, &u->raw_memchunk, &u->encoded_memchunk);
                            u->next_encoding_overhead = (u->encoded_memchunk.length - (rl - u->raw_memchunk.length));
                            u->encoding_ratio = u->encoded_memchunk.length / (rl - u->raw_memchunk.length);
                        } else {
                            /* We render some silence into our memchunk */
                            memcpy(&u->encoded_memchunk, &silence, sizeof(pa_memchunk));
                            pa_memblock_ref(silence.memblock);

                            /* Calculate/store some values to be used with the smoother */
                            u->next_encoding_overhead = silence_overhead;
                            u->encoding_ratio = silence_ratio;
                        }
                    }
                    pa_assert(u->encoded_memchunk.length > 0);

                    p = pa_memblock_acquire(u->encoded_memchunk.memblock);
                    l = pa_write(u->fd, (uint8_t*) p + u->encoded_memchunk.index, u->encoded_memchunk.length, &write_type);
                    pa_memblock_release(u->encoded_memchunk.memblock);

                    pa_assert(l != 0);

                    if (l < 0) {

                        if (errno == EINTR)
                            continue;
                        else if (errno == EAGAIN) {

                            /* OK, we filled all socket buffers up
                             * now. */
                            goto filled_up;

                        } else {
                            pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno));
                            goto fail;
                        }

                    } else {
                        u->offset += l;

                        u->encoded_memchunk.index += l;
                        u->encoded_memchunk.length -= l;

                        pollfd->revents = 0;

                        if (u->encoded_memchunk.length > 0) {
                            /* we've completely written the encoded data, so update our overhead */
                            u->encoding_overhead += u->next_encoding_overhead;

                            /* OK, we wrote less that we asked for,
                             * hence we can assume that the socket
                             * buffers are full now */
                            goto filled_up;
                        }
                    }
                }

filled_up:

                /* At this spot we know that the socket buffers are
                 * fully filled up. This is the best time to estimate
                 * the playback position of the server */

                n = u->offset - u->encoding_overhead;

#ifdef SIOCOUTQ
                {
                    int l;
                    if (ioctl(u->fd, SIOCOUTQ, &l) >= 0 && l > 0)
                        n -= (l / u->encoding_ratio);
                }
#endif

                usec = pa_bytes_to_usec(n, &u->sink->sample_spec);

                if (usec > u->latency)
                    usec -= u->latency;
                else
                    usec = 0;

                pa_smoother_put(u->smoother, pa_rtclock_now(), usec);
            }

            /* Hmm, nothing to do. Let's sleep */
            pollfd->events = POLLOUT; /*PA_SINK_IS_OPENED(u->sink->thread_info.state)  ? POLLOUT : 0;*/
        }

        if ((ret = pa_rtpoll_run(u->rtpoll)) < 0)
            goto fail;

        if (ret == 0)
            goto finish;

        if (u->rtpoll_item) {
            struct pollfd* pollfd;

            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);

            if (pollfd->revents & ~POLLOUT) {
                if (u->sink->thread_info.state != PA_SINK_SUSPENDED) {
                    pa_log("FIFO shutdown.");
                    goto fail;
                }

                /* We expect this to happen on occasion if we are not sending data.
                   It's perfectly natural and normal and natural */
                if (u->rtpoll_item)
                    pa_rtpoll_item_free(u->rtpoll_item);
                u->rtpoll_item = NULL;
            }
        }
    }

fail:
    /* If this was no regular exit from the loop we have to continue
     * processing messages until we received PA_MESSAGE_SHUTDOWN */
    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);

finish:
    if (silence.memblock)
        pa_memblock_unref(silence.memblock);
    pa_log_debug("Thread shutting down");
}
示例#23
0
/* Called from input thread context */
static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
    struct userdata *u;

    pa_source_output_assert_ref(o);
    pa_source_output_assert_io_context(o);
    pa_assert_se(u = o->userdata);

    if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output))) {
        pa_log("push when no link?");
        return;
    }

    /* PUT YOUR CODE HERE TO DO SOMETHING WITH THE SOURCE DATA */

    /* if uplink sink exists, pull data from there; simplify by using
       same length as chunk provided by source */
    if(u->sink && (pa_sink_get_state(u->sink) == PA_SINK_RUNNING)) {
        pa_memchunk tchunk;
        size_t nbytes = chunk->length;
        pa_mix_info streams[2];
        pa_memchunk target_chunk;
        void *target;
        int ch;

        /* Hmm, process any rewind request that might be queued up */
        pa_sink_process_rewind(u->sink, 0);

        /* get data from the sink */
        while (pa_memblockq_peek(u->sink_memblockq, &tchunk) < 0) {
            pa_memchunk nchunk;

            /* make sure we get nbytes from the sink with render_full,
               otherwise we cannot mix with the uplink */
            pa_sink_render_full(u->sink, nbytes, &nchunk);
            pa_memblockq_push(u->sink_memblockq, &nchunk);
            pa_memblock_unref(nchunk.memblock);
        }
        pa_assert(tchunk.length == chunk->length);

        /* move the read pointer for sink memblockq */
        pa_memblockq_drop(u->sink_memblockq, tchunk.length);

        /* allocate target chunk */
        /* this could probably be done in-place, but having chunk as both
           the input and output creates issues with reference counts */
        target_chunk.index = 0;
        target_chunk.length = chunk->length;
        pa_assert(target_chunk.length == chunk->length);

        target_chunk.memblock = pa_memblock_new(o->source->core->mempool,
                                                target_chunk.length);
        pa_assert( target_chunk.memblock );

        /* get target pointer */
        target = (void*)((uint8_t*)pa_memblock_acquire(target_chunk.memblock)
                         + target_chunk.index);

        /* set-up mixing structure
           volume was taken care of in sink and source already */
        streams[0].chunk = *chunk;
        for(ch=0;ch<o->sample_spec.channels;ch++)
            streams[0].volume.values[ch] = PA_VOLUME_NORM; /* FIXME */
        streams[0].volume.channels = o->sample_spec.channels;

        streams[1].chunk = tchunk;
        for(ch=0;ch<o->sample_spec.channels;ch++)
            streams[1].volume.values[ch] = PA_VOLUME_NORM; /* FIXME */
        streams[1].volume.channels = o->sample_spec.channels;

        /* do mixing */
        pa_mix(streams,                /* 2 streams to be mixed */
               2,
               target,                 /* put result in target chunk */
               chunk->length,          /* same length as input */
               (const pa_sample_spec *)&o->sample_spec, /* same sample spec for input and output */
               NULL,                   /* no volume information */
               FALSE);                 /* no mute */

        pa_memblock_release(target_chunk.memblock);
        pa_memblock_unref(tchunk.memblock); /* clean-up */

        /* forward the data to the virtual source */
        pa_source_post(u->source, &target_chunk);

        pa_memblock_unref(target_chunk.memblock); /* clean-up */

    } else {
        /* forward the data to the virtual source */
        pa_source_post(u->source, chunk);
    }


}
示例#24
0
static void thread_func(void *userdata) {
    struct userdata *u = userdata;
    int write_type = 0;

    pa_assert(u);

    pa_log_debug("Thread starting up");

    pa_thread_mq_install(&u->thread_mq);

    pa_smoother_set_time_offset(u->smoother, pa_rtclock_now());

    for (;;) {
        int ret;

        if (PA_SINK_IS_OPENED(u->sink->thread_info.state))
            if (u->sink->thread_info.rewind_requested)
                pa_sink_process_rewind(u->sink, 0);

        if (u->rtpoll_item) {
            struct pollfd *pollfd;
            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);

            /* Render some data and write it to the fifo */
            if (PA_SINK_IS_OPENED(u->sink->thread_info.state) && pollfd->revents) {
                pa_usec_t usec;
                int64_t n;

                for (;;) {
                    ssize_t l;
                    void *p;

                    if (u->memchunk.length <= 0)
                        pa_sink_render(u->sink, u->block_size, &u->memchunk);

                    pa_assert(u->memchunk.length > 0);

                    p = pa_memblock_acquire(u->memchunk.memblock);
                    l = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, &write_type);
                    pa_memblock_release(u->memchunk.memblock);

                    pa_assert(l != 0);

                    if (l < 0) {

                        if (errno == EINTR)
                            continue;
                        else if (errno == EAGAIN) {

                            /* OK, we filled all socket buffers up
                             * now. */
                            goto filled_up;

                        } else {
                            pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno));
                            goto fail;
                        }

                    } else {
                        u->offset += l;

                        u->memchunk.index += (size_t) l;
                        u->memchunk.length -= (size_t) l;

                        if (u->memchunk.length <= 0) {
                            pa_memblock_unref(u->memchunk.memblock);
                            pa_memchunk_reset(&u->memchunk);
                        }

                        pollfd->revents = 0;

                        if (u->memchunk.length > 0)

                            /* OK, we wrote less that we asked for,
                             * hence we can assume that the socket
                             * buffers are full now */
                            goto filled_up;
                    }
                }

            filled_up:

                /* At this spot we know that the socket buffers are
                 * fully filled up. This is the best time to estimate
                 * the playback position of the server */

                n = u->offset;

#ifdef SIOCOUTQ
                {
                    int l;
                    if (ioctl(u->fd, SIOCOUTQ, &l) >= 0 && l > 0)
                        n -= l;
                }
#endif

                usec = pa_bytes_to_usec((uint64_t) n, &u->sink->sample_spec);

                if (usec > u->latency)
                    usec -= u->latency;
                else
                    usec = 0;

                pa_smoother_put(u->smoother, pa_rtclock_now(), usec);
            }

            /* Hmm, nothing to do. Let's sleep */
            pollfd->events = (short) (PA_SINK_IS_OPENED(u->sink->thread_info.state) ? POLLOUT : 0);
        }

        if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
            goto fail;

        if (ret == 0)
            goto finish;

        if (u->rtpoll_item) {
            struct pollfd* pollfd;

            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);

            if (pollfd->revents & ~POLLOUT) {
                pa_log("FIFO shutdown.");
                goto fail;
            }
        }
    }

fail:
    /* If this was no regular exit from the loop we have to continue
     * processing messages until we received PA_MESSAGE_SHUTDOWN */
    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);

finish:
    pa_log_debug("Thread shutting down");
}
/* Called from I/O thread context */
static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
    struct userdata *u;
    float *src, *dst;
    unsigned n;
    pa_memchunk tchunk;

    unsigned j, k, l;
    float sum_right, sum_left;
    float current_sample;

    pa_sink_input_assert_ref(i);
    pa_assert(chunk);
    pa_assert_se(u = i->userdata);

    /* Hmm, process any rewind request that might be queued up */
    pa_sink_process_rewind(u->sink, 0);

    while (pa_memblockq_peek(u->memblockq, &tchunk) < 0) {
        pa_memchunk nchunk;

        pa_sink_render(u->sink, nbytes * u->sink_fs / u->fs, &nchunk);
        pa_memblockq_push(u->memblockq, &nchunk);
        pa_memblock_unref(nchunk.memblock);
    }

    tchunk.length = PA_MIN(nbytes * u->sink_fs / u->fs, tchunk.length);
    pa_assert(tchunk.length > 0);

    n = (unsigned) (tchunk.length / u->sink_fs);

    pa_assert(n > 0);

    chunk->index = 0;
    chunk->length = n * u->fs;
    chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length);

    pa_memblockq_drop(u->memblockq, n * u->sink_fs);

    src = pa_memblock_acquire_chunk(&tchunk);
    dst = pa_memblock_acquire(chunk->memblock);

    for (l = 0; l < n; l++) {
        memcpy(((char*) u->input_buffer) + u->input_buffer_offset * u->sink_fs, ((char *) src) + l * u->sink_fs, u->sink_fs);

        sum_right = 0;
        sum_left = 0;

        /* fold the input buffer with the impulse response */
        for (j = 0; j < u->hrir_samples; j++) {
            for (k = 0; k < u->channels; k++) {
                current_sample = u->input_buffer[((u->input_buffer_offset + j) % u->hrir_samples) * u->channels + k];

                sum_left += current_sample * u->hrir_data[j * u->hrir_channels + u->mapping_left[k]];
                sum_right += current_sample * u->hrir_data[j * u->hrir_channels + u->mapping_right[k]];
            }
        }

        dst[2 * l] = PA_CLAMP_UNLIKELY(sum_left, -1.0f, 1.0f);
        dst[2 * l + 1] = PA_CLAMP_UNLIKELY(sum_right, -1.0f, 1.0f);

        u->input_buffer_offset--;
        if (u->input_buffer_offset < 0)
            u->input_buffer_offset += u->hrir_samples;
    }

    pa_memblock_release(tchunk.memblock);
    pa_memblock_release(chunk->memblock);

    pa_memblock_unref(tchunk.memblock);

    return 0;
}