コード例 #1
0
pa_memchunk* pa_silence_memchunk_get(pa_silence_cache *cache, pa_mempool *pool, pa_memchunk* ret, const pa_sample_spec *spec, size_t length) {
    pa_memblock *b;
    size_t l;

    pa_assert(cache);
    pa_assert(pa_sample_spec_valid(spec));

    if (!(b = cache->blocks[spec->format]))

        switch (spec->format) {
            case PA_SAMPLE_U8:
                cache->blocks[PA_SAMPLE_U8] = b = silence_memblock_new(pool, 0x80);
                break;
            case PA_SAMPLE_S16LE:
            case PA_SAMPLE_S16BE:
            case PA_SAMPLE_S32LE:
            case PA_SAMPLE_S32BE:
            case PA_SAMPLE_S24LE:
            case PA_SAMPLE_S24BE:
            case PA_SAMPLE_S24_32LE:
            case PA_SAMPLE_S24_32BE:
            case PA_SAMPLE_FLOAT32LE:
            case PA_SAMPLE_FLOAT32BE:
                cache->blocks[PA_SAMPLE_S16LE] = b = silence_memblock_new(pool, 0);
                cache->blocks[PA_SAMPLE_S16BE] = pa_memblock_ref(b);
                cache->blocks[PA_SAMPLE_S32LE] = pa_memblock_ref(b);
                cache->blocks[PA_SAMPLE_S32BE] = pa_memblock_ref(b);
                cache->blocks[PA_SAMPLE_S24LE] = pa_memblock_ref(b);
                cache->blocks[PA_SAMPLE_S24BE] = pa_memblock_ref(b);
                cache->blocks[PA_SAMPLE_S24_32LE] = pa_memblock_ref(b);
                cache->blocks[PA_SAMPLE_S24_32BE] = pa_memblock_ref(b);
                cache->blocks[PA_SAMPLE_FLOAT32LE] = pa_memblock_ref(b);
                cache->blocks[PA_SAMPLE_FLOAT32BE] = pa_memblock_ref(b);
                break;
            case PA_SAMPLE_ALAW:
                cache->blocks[PA_SAMPLE_ALAW] = b = silence_memblock_new(pool, 0xd5);
                break;
            case PA_SAMPLE_ULAW:
                cache->blocks[PA_SAMPLE_ULAW] = b = silence_memblock_new(pool, 0xff);
                break;
            default:
                pa_assert_not_reached();
    }

    pa_assert(b);

    ret->memblock = pa_memblock_ref(b);

    l = pa_memblock_get_length(b);
    if (length > l || length == 0)
        length = l;

    ret->length = pa_frame_align(length, spec);
    ret->index = 0;

    return ret;
}
コード例 #2
0
ファイル: asyncmsgq.c プロジェクト: ford-prefect/pulseaudio
void pa_asyncmsgq_post(pa_asyncmsgq *a, pa_msgobject *object, int code, const void *userdata, int64_t offset, const pa_memchunk *chunk, pa_free_cb_t free_cb) {
    struct asyncmsgq_item *i;
    pa_assert(PA_REFCNT_VALUE(a) > 0);

    if (!(i = pa_flist_pop(PA_STATIC_FLIST_GET(asyncmsgq))))
        i = pa_xnew(struct asyncmsgq_item, 1);

    i->code = code;
    i->object = object ? pa_msgobject_ref(object) : NULL;
    i->userdata = (void*) userdata;
    i->free_cb = free_cb;
    i->offset = offset;
    if (chunk) {
        pa_assert(chunk->memblock);
        i->memchunk = *chunk;
        pa_memblock_ref(i->memchunk.memblock);
    } else
        pa_memchunk_reset(&i->memchunk);
    i->semaphore = NULL;

    /* This mutex makes the queue multiple-writer safe. This lock is only used on the writing side */
    pa_mutex_lock(a->mutex);
    pa_asyncq_post(a->asyncq, i);
    pa_mutex_unlock(a->mutex);
}
コード例 #3
0
ファイル: core-scache.c プロジェクト: KimT/pulseaudio_kt
int pa_scache_add_item(
        pa_core *c,
        const char *name,
        const pa_sample_spec *ss,
        const pa_channel_map *map,
        const pa_memchunk *chunk,
        pa_proplist *p,
        uint32_t *idx) {

    pa_scache_entry *e;
    char st[PA_SAMPLE_SPEC_SNPRINT_MAX];
    pa_channel_map tmap;

    pa_assert(c);
    pa_assert(name);
    pa_assert(!ss || pa_sample_spec_valid(ss));
    pa_assert(!map || (pa_channel_map_valid(map) && ss && pa_channel_map_compatible(map, ss)));

    if (ss && !map) {
        pa_channel_map_init_extend(&tmap, ss->channels, PA_CHANNEL_MAP_DEFAULT);
        map = &tmap;
    }

    if (chunk && chunk->length > PA_SCACHE_ENTRY_SIZE_MAX)
        return -1;

    if (!(e = scache_add_item(c, name)))
        return -1;

    pa_sample_spec_init(&e->sample_spec);
    pa_channel_map_init(&e->channel_map);
    pa_cvolume_init(&e->volume);
    e->volume_is_set = FALSE;

    if (ss) {
        e->sample_spec = *ss;
        pa_cvolume_reset(&e->volume, ss->channels);
    }

    if (map)
        e->channel_map = *map;

    if (chunk) {
        e->memchunk = *chunk;
        pa_memblock_ref(e->memchunk.memblock);
    }

    if (p)
        pa_proplist_update(e->proplist, PA_UPDATE_REPLACE, p);

    if (idx)
        *idx = e->index;

    pa_log_debug("Created sample \"%s\" (#%d), %lu bytes with sample spec %s",
                 name, e->index, (unsigned long) e->memchunk.length,
                 pa_sample_spec_snprint(st, sizeof(st), &e->sample_spec));

    return 0;
}
コード例 #4
0
ファイル: memblock.c プロジェクト: DryakhlyyZlodey/pulseaudio
/* Self-locked */
pa_memblock* pa_memimport_get(pa_memimport *i, uint32_t block_id, uint32_t shm_id, size_t offset, size_t size) {
    pa_memblock *b = NULL;
    pa_memimport_segment *seg;

    pa_assert(i);

    pa_mutex_lock(i->mutex);

    if ((b = pa_hashmap_get(i->blocks, PA_UINT32_TO_PTR(block_id)))) {
        pa_memblock_ref(b);
        goto finish;
    }

    if (pa_hashmap_size(i->blocks) >= PA_MEMIMPORT_SLOTS_MAX)
        goto finish;

    if (!(seg = pa_hashmap_get(i->segments, PA_UINT32_TO_PTR(shm_id))))
        if (!(seg = segment_attach(i, shm_id)))
            goto finish;

    if (offset+size > seg->memory.size)
        goto finish;

    if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks))))
        b = pa_xnew(pa_memblock, 1);

    PA_REFCNT_INIT(b);
    b->pool = i->pool;
    b->type = PA_MEMBLOCK_IMPORTED;
    b->read_only = true;
    b->is_silence = false;
    pa_atomic_ptr_store(&b->data, (uint8_t*) seg->memory.ptr + offset);
    b->length = size;
    pa_atomic_store(&b->n_acquired, 0);
    pa_atomic_store(&b->please_signal, 0);
    b->per_type.imported.id = block_id;
    b->per_type.imported.segment = seg;

    pa_hashmap_put(i->blocks, PA_UINT32_TO_PTR(block_id), b);

    seg->n_blocks++;

    stat_add(b);

finish:
    pa_mutex_unlock(i->mutex);

    return b;
}
コード例 #5
0
ファイル: memblockq.c プロジェクト: a-darwish/pulseaudio
pa_memblockq* pa_memblockq_new(
        const char *name,
        int64_t idx,
        size_t maxlength,
        size_t tlength,
        const pa_sample_spec *sample_spec,
        size_t prebuf,
        size_t minreq,
        size_t maxrewind,
        pa_memchunk *silence) {

    pa_memblockq* bq;

    pa_assert(sample_spec);
    pa_assert(name);

    bq = pa_xnew0(pa_memblockq, 1);
    bq->name = pa_xstrdup(name);

    bq->sample_spec = *sample_spec;
    bq->base = pa_frame_size(sample_spec);
    bq->read_index = bq->write_index = idx;

    pa_log_debug("memblockq requested: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu maxrewind=%lu",
                 (unsigned long) maxlength, (unsigned long) tlength, (unsigned long) bq->base, (unsigned long) prebuf, (unsigned long) minreq, (unsigned long) maxrewind);

    bq->in_prebuf = true;

    pa_memblockq_set_maxlength(bq, maxlength);
    pa_memblockq_set_tlength(bq, tlength);
    pa_memblockq_set_minreq(bq, minreq);
    pa_memblockq_set_prebuf(bq, prebuf);
    pa_memblockq_set_maxrewind(bq, maxrewind);

    pa_log_debug("memblockq sanitized: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu maxrewind=%lu",
                 (unsigned long) bq->maxlength, (unsigned long) bq->tlength, (unsigned long) bq->base, (unsigned long) bq->prebuf, (unsigned long) bq->minreq, (unsigned long) bq->maxrewind);

    if (silence) {
        bq->silence = *silence;
        pa_memblock_ref(bq->silence.memblock);
    }

    bq->mcalign = pa_mcalign_new(bq->base);

    return bq;
}
コード例 #6
0
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_se(u = i->userdata);
    pa_assert(chunk);

    *chunk = u->memchunk;
    pa_memblock_ref(chunk->memblock);

    chunk->index += u->peek_index;
    chunk->length -= u->peek_index;

    u->peek_index = 0;

    return 0;
}
int voice_init_voip_sink(struct userdata *u, const char *name) {
    pa_sink_new_data sink_data;
    pa_assert(u);
    pa_assert(u->core);
    pa_assert(u->master_sink);
    ENTER();

    pa_sink_new_data_init(&sink_data);
    sink_data.module = u->module;
    sink_data.driver = __FILE__;
    pa_sink_new_data_set_name(&sink_data, name);
    pa_sink_new_data_set_sample_spec(&sink_data, &u->aep_sample_spec);
    pa_sink_new_data_set_channel_map(&sink_data, &u->aep_channel_map);
    pa_proplist_setf(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "%s connected conceptually to %s", name, u->raw_sink->name);
    pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, u->raw_sink->name);
    pa_proplist_sets(sink_data.proplist, "module-suspend-on-idle.timeout", "1");

    pa_proplist_sets(sink_data.proplist, PA_PROP_SINK_API_EXTENSION_PROPERTY_NAME,
                     PA_PROP_SINK_API_EXTENSION_PROPERTY_VALUE);

    u->voip_sink = pa_sink_new(u->core, &sink_data,
                               (u->master_sink->flags & (PA_SINK_LATENCY | PA_SINK_DYNAMIC_LATENCY)) | PA_SINK_SHARE_VOLUME_WITH_MASTER);

    pa_sink_new_data_done(&sink_data);

    /* Create sink */
    if (!u->voip_sink) {
        pa_log_error("Failed to create sink.");
        return -1;
    }

    u->voip_sink->parent.process_msg = voip_sink_process_msg;
    u->voip_sink->set_state = voip_sink_set_state;
    u->voip_sink->update_requested_latency = voip_sink_update_requested_latency;
    u->voip_sink->request_rewind = voip_sink_request_rewind;
    u->voip_sink->userdata = u;
    pa_memblock_ref(u->aep_silence_memchunk.memblock);
    u->voip_sink->silence = u->aep_silence_memchunk;

    pa_sink_set_asyncmsgq(u->voip_sink, u->master_sink->asyncmsgq);
    pa_sink_set_rtpoll(u->voip_sink, u->master_sink->thread_info.rtpoll);

    return 0;
}
コード例 #8
0
ファイル: memblock.c プロジェクト: DryakhlyyZlodey/pulseaudio
/* No lock necessary */
static pa_memblock *memblock_shared_copy(pa_mempool *p, pa_memblock *b) {
    pa_memblock *n;

    pa_assert(p);
    pa_assert(b);

    if (b->type == PA_MEMBLOCK_IMPORTED ||
        b->type == PA_MEMBLOCK_POOL ||
        b->type == PA_MEMBLOCK_POOL_EXTERNAL) {
        pa_assert(b->pool == p);
        return pa_memblock_ref(b);
    }

    if (!(n = pa_memblock_new_pool(p, b->length)))
        return NULL;

    memcpy(pa_atomic_ptr_load(&n->data), pa_atomic_ptr_load(&b->data), b->length);
    return n;
}
コード例 #9
0
ファイル: srbchannel.c プロジェクト: asceticmt/pulseaudio
pa_srbchannel* pa_srbchannel_new_from_template(pa_mainloop_api *m, pa_srbchannel_template *t)
{
    int temp;
    struct srbheader *srh;
    pa_srbchannel* sr = pa_xmalloc0(sizeof(pa_srbchannel));

    sr->mainloop = m;
    sr->memblock = t->memblock;
    pa_memblock_ref(sr->memblock);
    srh = pa_memblock_acquire(sr->memblock);

    sr->rb_read.capacity = sr->rb_write.capacity = srh->capacity;
    sr->rb_read.count = &srh->read_count;
    sr->rb_write.count = &srh->write_count;

    sr->rb_read.memory = (uint8_t*) srh + srh->readbuf_offset;
    sr->rb_write.memory = (uint8_t*) srh + srh->writebuf_offset;

    sr->sem_read = pa_fdsem_open_shm(&srh->read_semdata, t->readfd);
    if (!sr->sem_read)
        goto fail;

    sr->sem_write = pa_fdsem_open_shm(&srh->write_semdata, t->writefd);
    if (!sr->sem_write)
        goto fail;

    pa_srbchannel_swap(sr);
    temp = t->readfd; t->readfd = t->writefd; t->writefd = temp;

#ifdef DEBUG_SRBCHANNEL
    pa_log("Enabling io event on fd %d", t->readfd);
#endif

    sr->read_event = m->io_new(m, t->readfd, PA_IO_EVENT_INPUT, semread_cb, sr);
    m->io_enable(sr->read_event, PA_IO_EVENT_INPUT);

    return sr;

fail:
    pa_srbchannel_free(sr);

    return NULL;
}
コード例 #10
0
ファイル: context.c プロジェクト: Wezl/pulseaudio
static void handle_srbchannel_memblock(pa_context *c, pa_memblock *memblock) {
    pa_srbchannel *sr;
    pa_tagstruct *t;

    pa_assert(c);

    /* Memblock sanity check */
    if (!memblock) {
        pa_context_fail(c, PA_ERR_PROTOCOL);
        return;
    } else if (pa_memblock_is_read_only(memblock)) {
        pa_context_fail(c, PA_ERR_PROTOCOL);
        return;
    } else if (pa_memblock_is_ours(memblock)) {
        pa_context_fail(c, PA_ERR_PROTOCOL);
        return;
    }

    /* Create the srbchannel */
    c->srb_template.memblock = memblock;
    pa_memblock_ref(memblock);
    sr = pa_srbchannel_new_from_template(c->mainloop, &c->srb_template);
    if (!sr) {
        pa_log_warn("Failed to create srbchannel from template");
        c->srb_template.readfd = -1;
        c->srb_template.writefd = -1;
        pa_memblock_unref(c->srb_template.memblock);
        c->srb_template.memblock = NULL;
        return;
    }

    /* Ack the enable command */
    t = pa_tagstruct_new();
    pa_tagstruct_putu32(t, PA_COMMAND_ENABLE_SRBCHANNEL);
    pa_tagstruct_putu32(t, c->srb_setup_tag);
    pa_pstream_send_tagstruct(c->pstream, t);

    /* ...and switch over */
    pa_pstream_set_srbchannel(c->pstream, sr);
}
コード例 #11
0
ファイル: memblockq.c プロジェクト: a-darwish/pulseaudio
int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {
    struct list_item *q, *n;
    pa_memchunk chunk;
    int64_t old;

    pa_assert(bq);
    pa_assert(uchunk);
    pa_assert(uchunk->memblock);
    pa_assert(uchunk->length > 0);
    pa_assert(uchunk->index + uchunk->length <= pa_memblock_get_length(uchunk->memblock));

    pa_assert(uchunk->length % bq->base == 0);
    pa_assert(uchunk->index % bq->base == 0);

    if (!can_push(bq, uchunk->length))
        return -1;

    old = bq->write_index;
    chunk = *uchunk;

    fix_current_write(bq);
    q = bq->current_write;

    /* First we advance the q pointer right of where we want to
     * write to */

    if (q) {
        while (bq->write_index + (int64_t) chunk.length > q->index)
            if (q->next)
                q = q->next;
            else
                break;
    }

    if (!q)
        q = bq->blocks_tail;

    /* We go from back to front to look for the right place to add
     * this new entry. Drop data we will overwrite on the way */

    while (q) {

        if (bq->write_index >= q->index + (int64_t) q->chunk.length)
            /* We found the entry where we need to place the new entry immediately after */
            break;
        else if (bq->write_index + (int64_t) chunk.length <= q->index) {
            /* This entry isn't touched at all, let's skip it */
            q = q->prev;
        } else if (bq->write_index <= q->index &&
                   bq->write_index + (int64_t) chunk.length >= q->index + (int64_t) q->chunk.length) {

            /* This entry is fully replaced by the new entry, so let's drop it */

            struct list_item *p;
            p = q;
            q = q->prev;
            drop_block(bq, p);
        } else if (bq->write_index >= q->index) {
            /* The write index points into this memblock, so let's
             * truncate or split it */

            if (bq->write_index + (int64_t) chunk.length < q->index + (int64_t) q->chunk.length) {

                /* We need to save the end of this memchunk */
                struct list_item *p;
                size_t d;

                /* Create a new list entry for the end of the memchunk */
                if (!(p = pa_flist_pop(PA_STATIC_FLIST_GET(list_items))))
                    p = pa_xnew(struct list_item, 1);

                p->chunk = q->chunk;
                pa_memblock_ref(p->chunk.memblock);

                /* Calculate offset */
                d = (size_t) (bq->write_index + (int64_t) chunk.length - q->index);
                pa_assert(d > 0);

                /* Drop it from the new entry */
                p->index = q->index + (int64_t) d;
                p->chunk.length -= d;

                /* Add it to the list */
                p->prev = q;
                if ((p->next = q->next))
                    q->next->prev = p;
                else
                    bq->blocks_tail = p;
                q->next = p;

                bq->n_blocks++;
            }

            /* Truncate the chunk */
            if (!(q->chunk.length = (size_t) (bq->write_index - q->index))) {
                struct list_item *p;
                p = q;
                q = q->prev;
                drop_block(bq, p);
            }

            /* We had to truncate this block, hence we're now at the right position */
            break;
        } else {
            size_t d;

            pa_assert(bq->write_index + (int64_t)chunk.length > q->index &&
                      bq->write_index + (int64_t)chunk.length < q->index + (int64_t)q->chunk.length &&
                      bq->write_index < q->index);

            /* The job overwrites the current entry at the end, so let's drop the beginning of this entry */

            d = (size_t) (bq->write_index + (int64_t) chunk.length - q->index);
            q->index += (int64_t) d;
            q->chunk.index += d;
            q->chunk.length -= d;

            q = q->prev;
        }
    }
コード例 #12
0
ファイル: module-raop-sink.c プロジェクト: lebauce/pulseaudio
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");
}