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; }
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); }
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; }
/* 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; }
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; }
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; }
/* 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; }
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; }
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); }
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; } }
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"); }