mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-11-11 13:30:02 -05:00
merge glitch-free branch back into trunk
git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@2445 fefdeb5f-60dc-0310-8127-8f9354f1896f
This commit is contained in:
parent
91f092eadc
commit
045c1d602d
189 changed files with 12559 additions and 4959 deletions
|
|
@ -136,7 +136,7 @@ void pa_asyncmsgq_post(pa_asyncmsgq *a, pa_msgobject *object, int code, const vo
|
|||
|
||||
/* This mutex makes the queue multiple-writer safe. This lock is only used on the writing side */
|
||||
pa_mutex_lock(a->mutex);
|
||||
pa_assert_se(pa_asyncq_push(a->asyncq, i, 1) == 0);
|
||||
pa_asyncq_post(a->asyncq, i);
|
||||
pa_mutex_unlock(a->mutex);
|
||||
}
|
||||
|
||||
|
|
@ -163,7 +163,7 @@ int pa_asyncmsgq_send(pa_asyncmsgq *a, pa_msgobject *object, int code, const voi
|
|||
|
||||
/* Thus mutex makes the queue multiple-writer safe. This lock is only used on the writing side */
|
||||
pa_mutex_lock(a->mutex);
|
||||
pa_assert_se(pa_asyncq_push(a->asyncq, &i, 1) == 0);
|
||||
pa_assert_se(pa_asyncq_push(a->asyncq, &i, TRUE) == 0);
|
||||
pa_mutex_unlock(a->mutex);
|
||||
|
||||
pa_semaphore_wait(i.semaphore);
|
||||
|
|
@ -174,7 +174,7 @@ int pa_asyncmsgq_send(pa_asyncmsgq *a, pa_msgobject *object, int code, const voi
|
|||
return i.ret;
|
||||
}
|
||||
|
||||
int pa_asyncmsgq_get(pa_asyncmsgq *a, pa_msgobject **object, int *code, void **userdata, int64_t *offset, pa_memchunk *chunk, int wait) {
|
||||
int pa_asyncmsgq_get(pa_asyncmsgq *a, pa_msgobject **object, int *code, void **userdata, int64_t *offset, pa_memchunk *chunk, pa_bool_t wait) {
|
||||
pa_assert(PA_REFCNT_VALUE(a) > 0);
|
||||
pa_assert(!a->current);
|
||||
|
||||
|
|
@ -276,22 +276,40 @@ int pa_asyncmsgq_process_one(pa_asyncmsgq *a) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
int pa_asyncmsgq_get_fd(pa_asyncmsgq *a) {
|
||||
int pa_asyncmsgq_read_fd(pa_asyncmsgq *a) {
|
||||
pa_assert(PA_REFCNT_VALUE(a) > 0);
|
||||
|
||||
return pa_asyncq_get_fd(a->asyncq);
|
||||
return pa_asyncq_read_fd(a->asyncq);
|
||||
}
|
||||
|
||||
int pa_asyncmsgq_before_poll(pa_asyncmsgq *a) {
|
||||
int pa_asyncmsgq_read_before_poll(pa_asyncmsgq *a) {
|
||||
pa_assert(PA_REFCNT_VALUE(a) > 0);
|
||||
|
||||
return pa_asyncq_before_poll(a->asyncq);
|
||||
return pa_asyncq_read_before_poll(a->asyncq);
|
||||
}
|
||||
|
||||
void pa_asyncmsgq_after_poll(pa_asyncmsgq *a) {
|
||||
void pa_asyncmsgq_read_after_poll(pa_asyncmsgq *a) {
|
||||
pa_assert(PA_REFCNT_VALUE(a) > 0);
|
||||
|
||||
pa_asyncq_after_poll(a->asyncq);
|
||||
pa_asyncq_read_after_poll(a->asyncq);
|
||||
}
|
||||
|
||||
int pa_asyncmsgq_write_fd(pa_asyncmsgq *a) {
|
||||
pa_assert(PA_REFCNT_VALUE(a) > 0);
|
||||
|
||||
return pa_asyncq_write_fd(a->asyncq);
|
||||
}
|
||||
|
||||
void pa_asyncmsgq_write_before_poll(pa_asyncmsgq *a) {
|
||||
pa_assert(PA_REFCNT_VALUE(a) > 0);
|
||||
|
||||
pa_asyncq_write_before_poll(a->asyncq);
|
||||
}
|
||||
|
||||
void pa_asyncmsgq_write_after_poll(pa_asyncmsgq *a) {
|
||||
pa_assert(PA_REFCNT_VALUE(a) > 0);
|
||||
|
||||
pa_asyncq_write_after_poll(a->asyncq);
|
||||
}
|
||||
|
||||
int pa_asyncmsgq_dispatch(pa_msgobject *object, int code, void *userdata, int64_t offset, pa_memchunk *memchunk) {
|
||||
|
|
|
|||
|
|
@ -56,20 +56,26 @@ typedef struct pa_asyncmsgq pa_asyncmsgq;
|
|||
|
||||
pa_asyncmsgq* pa_asyncmsgq_new(unsigned size);
|
||||
pa_asyncmsgq* pa_asyncmsgq_ref(pa_asyncmsgq *q);
|
||||
|
||||
void pa_asyncmsgq_unref(pa_asyncmsgq* q);
|
||||
|
||||
void pa_asyncmsgq_post(pa_asyncmsgq *q, pa_msgobject *object, int code, const void *userdata, int64_t offset, const pa_memchunk *memchunk, pa_free_cb_t userdata_free_cb);
|
||||
int pa_asyncmsgq_send(pa_asyncmsgq *q, pa_msgobject *object, int code, const void *userdata, int64_t offset, const pa_memchunk *memchunk);
|
||||
|
||||
int pa_asyncmsgq_get(pa_asyncmsgq *q, pa_msgobject **object, int *code, void **userdata, int64_t *offset, pa_memchunk *memchunk, int wait);
|
||||
int pa_asyncmsgq_get(pa_asyncmsgq *q, pa_msgobject **object, int *code, void **userdata, int64_t *offset, pa_memchunk *memchunk, pa_bool_t wait);
|
||||
int pa_asyncmsgq_dispatch(pa_msgobject *object, int code, void *userdata, int64_t offset, pa_memchunk *memchunk);
|
||||
void pa_asyncmsgq_done(pa_asyncmsgq *q, int ret);
|
||||
int pa_asyncmsgq_wait_for(pa_asyncmsgq *a, int code);
|
||||
int pa_asyncmsgq_process_one(pa_asyncmsgq *a);
|
||||
|
||||
/* Just for the reading side */
|
||||
int pa_asyncmsgq_get_fd(pa_asyncmsgq *q);
|
||||
int pa_asyncmsgq_before_poll(pa_asyncmsgq *a);
|
||||
void pa_asyncmsgq_after_poll(pa_asyncmsgq *a);
|
||||
/* For the reading side */
|
||||
int pa_asyncmsgq_read_fd(pa_asyncmsgq *q);
|
||||
int pa_asyncmsgq_read_before_poll(pa_asyncmsgq *a);
|
||||
void pa_asyncmsgq_read_after_poll(pa_asyncmsgq *a);
|
||||
|
||||
/* For the write side */
|
||||
int pa_asyncmsgq_write_fd(pa_asyncmsgq *q);
|
||||
void pa_asyncmsgq_write_before_poll(pa_asyncmsgq *a);
|
||||
void pa_asyncmsgq_write_after_poll(pa_asyncmsgq *a);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
/***
|
||||
This file is part of PulseAudio.
|
||||
|
||||
Copyright 2006 Lennart Poettering
|
||||
Copyright 2006-2008 Lennart Poettering
|
||||
|
||||
PulseAudio is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as
|
||||
|
|
@ -33,14 +33,16 @@
|
|||
#include <pulsecore/thread.h>
|
||||
#include <pulsecore/macro.h>
|
||||
#include <pulsecore/core-util.h>
|
||||
#include <pulsecore/llist.h>
|
||||
#include <pulsecore/flist.h>
|
||||
#include <pulse/xmalloc.h>
|
||||
|
||||
#include "asyncq.h"
|
||||
#include "fdsem.h"
|
||||
|
||||
#define ASYNCQ_SIZE 128
|
||||
#define ASYNCQ_SIZE 256
|
||||
|
||||
/* For debugging purposes we can define _Y to put and extra thread
|
||||
/* For debugging purposes we can define _Y to put an extra thread
|
||||
* yield between each operation. */
|
||||
|
||||
/* #define PROFILE */
|
||||
|
|
@ -51,18 +53,25 @@
|
|||
#define _Y do { } while(0)
|
||||
#endif
|
||||
|
||||
struct localq {
|
||||
void *data;
|
||||
PA_LLIST_FIELDS(struct localq);
|
||||
};
|
||||
|
||||
struct pa_asyncq {
|
||||
unsigned size;
|
||||
unsigned read_idx;
|
||||
unsigned write_idx;
|
||||
pa_fdsem *read_fdsem, *write_fdsem;
|
||||
|
||||
PA_LLIST_HEAD(struct localq, localq);
|
||||
struct localq *last_localq;
|
||||
pa_bool_t waiting_for_post;
|
||||
};
|
||||
|
||||
#define PA_ASYNCQ_CELLS(x) ((pa_atomic_ptr_t*) ((uint8_t*) (x) + PA_ALIGN(sizeof(struct pa_asyncq))))
|
||||
PA_STATIC_FLIST_DECLARE(localq, 0, pa_xfree);
|
||||
|
||||
static int is_power_of_two(unsigned size) {
|
||||
return !(size & (size - 1));
|
||||
}
|
||||
#define PA_ASYNCQ_CELLS(x) ((pa_atomic_ptr_t*) ((uint8_t*) (x) + PA_ALIGN(sizeof(struct pa_asyncq))))
|
||||
|
||||
static int reduce(pa_asyncq *l, int value) {
|
||||
return value & (unsigned) (l->size - 1);
|
||||
|
|
@ -74,12 +83,16 @@ pa_asyncq *pa_asyncq_new(unsigned size) {
|
|||
if (!size)
|
||||
size = ASYNCQ_SIZE;
|
||||
|
||||
pa_assert(is_power_of_two(size));
|
||||
pa_assert(pa_is_power_of_two(size));
|
||||
|
||||
l = pa_xmalloc0(PA_ALIGN(sizeof(pa_asyncq)) + (sizeof(pa_atomic_ptr_t) * size));
|
||||
|
||||
l->size = size;
|
||||
|
||||
PA_LLIST_HEAD_INIT(struct localq, l->localq);
|
||||
l->last_localq = NULL;
|
||||
l->waiting_for_post = FALSE;
|
||||
|
||||
if (!(l->read_fdsem = pa_fdsem_new())) {
|
||||
pa_xfree(l);
|
||||
return NULL;
|
||||
|
|
@ -95,6 +108,7 @@ pa_asyncq *pa_asyncq_new(unsigned size) {
|
|||
}
|
||||
|
||||
void pa_asyncq_free(pa_asyncq *l, pa_free_cb_t free_cb) {
|
||||
struct localq *q;
|
||||
pa_assert(l);
|
||||
|
||||
if (free_cb) {
|
||||
|
|
@ -104,12 +118,22 @@ void pa_asyncq_free(pa_asyncq *l, pa_free_cb_t free_cb) {
|
|||
free_cb(p);
|
||||
}
|
||||
|
||||
while ((q = l->localq)) {
|
||||
if (free_cb)
|
||||
free_cb(q->data);
|
||||
|
||||
PA_LLIST_REMOVE(struct localq, l->localq, q);
|
||||
|
||||
if (pa_flist_push(PA_STATIC_FLIST_GET(localq), q) < 0)
|
||||
pa_xfree(q);
|
||||
}
|
||||
|
||||
pa_fdsem_free(l->read_fdsem);
|
||||
pa_fdsem_free(l->write_fdsem);
|
||||
pa_xfree(l);
|
||||
}
|
||||
|
||||
int pa_asyncq_push(pa_asyncq*l, void *p, int wait) {
|
||||
static int push(pa_asyncq*l, void *p, pa_bool_t wait) {
|
||||
int idx;
|
||||
pa_atomic_ptr_t *cells;
|
||||
|
||||
|
|
@ -141,7 +165,63 @@ int pa_asyncq_push(pa_asyncq*l, void *p, int wait) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
void* pa_asyncq_pop(pa_asyncq*l, int wait) {
|
||||
static pa_bool_t flush_postq(pa_asyncq *l) {
|
||||
struct localq *q;
|
||||
|
||||
pa_assert(l);
|
||||
|
||||
while ((q = l->last_localq)) {
|
||||
|
||||
if (push(l, q->data, FALSE) < 0)
|
||||
return FALSE;
|
||||
|
||||
l->last_localq = q->prev;
|
||||
|
||||
PA_LLIST_REMOVE(struct localq, l->localq, q);
|
||||
|
||||
if (pa_flist_push(PA_STATIC_FLIST_GET(localq), q) < 0)
|
||||
pa_xfree(q);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int pa_asyncq_push(pa_asyncq*l, void *p, pa_bool_t wait) {
|
||||
pa_assert(l);
|
||||
|
||||
if (!flush_postq(l))
|
||||
return -1;
|
||||
|
||||
return push(l, p, wait);
|
||||
}
|
||||
|
||||
void pa_asyncq_post(pa_asyncq*l, void *p) {
|
||||
struct localq *q;
|
||||
|
||||
pa_assert(l);
|
||||
pa_assert(p);
|
||||
|
||||
if (pa_asyncq_push(l, p, FALSE) >= 0)
|
||||
return;
|
||||
|
||||
/* OK, we couldn't push anything in the queue. So let's queue it
|
||||
* locally and push it later */
|
||||
|
||||
pa_log("q overrun, queuing locally");
|
||||
|
||||
if (!(q = pa_flist_pop(PA_STATIC_FLIST_GET(localq))))
|
||||
q = pa_xnew(struct localq, 1);
|
||||
|
||||
q->data = p;
|
||||
PA_LLIST_PREPEND(struct localq, l->localq, q);
|
||||
|
||||
if (!l->last_localq)
|
||||
l->last_localq = q;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void* pa_asyncq_pop(pa_asyncq*l, pa_bool_t wait) {
|
||||
int idx;
|
||||
void *ret;
|
||||
pa_atomic_ptr_t *cells;
|
||||
|
|
@ -178,13 +258,13 @@ void* pa_asyncq_pop(pa_asyncq*l, int wait) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
int pa_asyncq_get_fd(pa_asyncq *q) {
|
||||
int pa_asyncq_read_fd(pa_asyncq *q) {
|
||||
pa_assert(q);
|
||||
|
||||
return pa_fdsem_get(q->write_fdsem);
|
||||
}
|
||||
|
||||
int pa_asyncq_before_poll(pa_asyncq *l) {
|
||||
int pa_asyncq_read_before_poll(pa_asyncq *l) {
|
||||
int idx;
|
||||
pa_atomic_ptr_t *cells;
|
||||
|
||||
|
|
@ -206,8 +286,38 @@ int pa_asyncq_before_poll(pa_asyncq *l) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
void pa_asyncq_after_poll(pa_asyncq *l) {
|
||||
void pa_asyncq_read_after_poll(pa_asyncq *l) {
|
||||
pa_assert(l);
|
||||
|
||||
pa_fdsem_after_poll(l->write_fdsem);
|
||||
}
|
||||
|
||||
int pa_asyncq_write_fd(pa_asyncq *q) {
|
||||
pa_assert(q);
|
||||
|
||||
return pa_fdsem_get(q->read_fdsem);
|
||||
}
|
||||
|
||||
void pa_asyncq_write_before_poll(pa_asyncq *l) {
|
||||
pa_assert(l);
|
||||
|
||||
for (;;) {
|
||||
|
||||
if (flush_postq(l))
|
||||
break;
|
||||
|
||||
if (pa_fdsem_before_poll(l->read_fdsem) >= 0) {
|
||||
l->waiting_for_post = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pa_asyncq_write_after_poll(pa_asyncq *l) {
|
||||
pa_assert(l);
|
||||
|
||||
if (l->waiting_for_post) {
|
||||
pa_fdsem_after_poll(l->read_fdsem);
|
||||
l->waiting_for_post = FALSE;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
#include <sys/types.h>
|
||||
#include <pulse/def.h>
|
||||
#include <pulsecore/macro.h>
|
||||
|
||||
/* A simple, asynchronous, lock-free (if requested also wait-free)
|
||||
* queue. Not multiple-reader/multiple-writer safe. If that is
|
||||
|
|
@ -46,11 +47,21 @@ typedef struct pa_asyncq pa_asyncq;
|
|||
pa_asyncq* pa_asyncq_new(unsigned size);
|
||||
void pa_asyncq_free(pa_asyncq* q, pa_free_cb_t free_cb);
|
||||
|
||||
void* pa_asyncq_pop(pa_asyncq *q, int wait);
|
||||
int pa_asyncq_push(pa_asyncq *q, void *p, int wait);
|
||||
void* pa_asyncq_pop(pa_asyncq *q, pa_bool_t wait);
|
||||
int pa_asyncq_push(pa_asyncq *q, void *p, pa_bool_t wait);
|
||||
|
||||
int pa_asyncq_get_fd(pa_asyncq *q);
|
||||
int pa_asyncq_before_poll(pa_asyncq *a);
|
||||
void pa_asyncq_after_poll(pa_asyncq *a);
|
||||
/* Similar to pa_asyncq_push(), but if the queue is full, postpone it
|
||||
* locally and delay until pa_asyncq_before_poll_post() */
|
||||
void pa_asyncq_post(pa_asyncq*l, void *p);
|
||||
|
||||
/* For the reading side */
|
||||
int pa_asyncq_read_fd(pa_asyncq *q);
|
||||
int pa_asyncq_read_before_poll(pa_asyncq *a);
|
||||
void pa_asyncq_read_after_poll(pa_asyncq *a);
|
||||
|
||||
/* For the writing side */
|
||||
int pa_asyncq_write_fd(pa_asyncq *q);
|
||||
void pa_asyncq_write_before_poll(pa_asyncq *a);
|
||||
void pa_asyncq_write_after_poll(pa_asyncq *a);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -90,6 +90,7 @@ static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
|
|||
static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
|
||||
static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
|
||||
static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
|
||||
static int pa_cli_command_describe(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
|
||||
static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
|
||||
static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
|
||||
static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
|
||||
|
|
@ -136,6 +137,7 @@ static const struct command commands[] = {
|
|||
{ "list", pa_cli_command_info, NULL, 1 },
|
||||
{ "load-module", pa_cli_command_load, "Load a module (args: name, arguments)", 3},
|
||||
{ "unload-module", pa_cli_command_unload, "Unload a module (args: index)", 2},
|
||||
{ "describe-module", pa_cli_command_describe, "Describe a module (arg: name)", 2},
|
||||
{ "set-sink-volume", pa_cli_command_sink_volume, "Set the volume of a sink (args: index|name, volume)", 3},
|
||||
{ "set-sink-input-volume", pa_cli_command_sink_input_volume, "Set the volume of a sink input (args: index, volume)", 3},
|
||||
{ "set-source-volume", pa_cli_command_source_volume, "Set the volume of a source (args: index|name, volume)", 3},
|
||||
|
|
@ -155,10 +157,10 @@ static const struct command commands[] = {
|
|||
{ "load-sample-dir-lazy", pa_cli_command_scache_load_dir, "Lazily load all files in a directory into the sample cache (args: pathname)", 2},
|
||||
{ "play-file", pa_cli_command_play_file, "Play a sound file (args: filename, sink|index)", 3},
|
||||
{ "list-autoload", pa_cli_command_autoload_list, "List autoload entries", 1},
|
||||
{ "add-autoload-sink", pa_cli_command_autoload_add, "Add autoload entry for a sink (args: sink, module name, arguments)", 4},
|
||||
{ "add-autoload-source", pa_cli_command_autoload_add, "Add autoload entry for a source (args: source, module name, arguments)", 4},
|
||||
{ "remove-autoload-sink", pa_cli_command_autoload_remove, "Remove autoload entry for a sink (args: name)", 2},
|
||||
{ "remove-autoload-source", pa_cli_command_autoload_remove, "Remove autoload entry for a source (args: name)", 2},
|
||||
{ "add-autoload-sink", pa_cli_command_autoload_add, NULL /*"Add autoload entry for a sink (args: sink, module name, arguments)"*/, 4},
|
||||
{ "add-autoload-source", pa_cli_command_autoload_add, NULL /*"Add autoload entry for a source (args: source, module name, arguments)"*/, 4},
|
||||
{ "remove-autoload-sink", pa_cli_command_autoload_remove, NULL /*"Remove autoload entry for a sink (args: name)"*/, 2},
|
||||
{ "remove-autoload-source", pa_cli_command_autoload_remove, NULL /*"Remove autoload entry for a source (args: name)"*/, 2},
|
||||
{ "dump", pa_cli_command_dump, "Dump daemon configuration", 1},
|
||||
{ "list-props", pa_cli_command_list_props, NULL, 1},
|
||||
{ "move-sink-input", pa_cli_command_move_sink_input, "Move sink input to another sink (args: index, sink)", 3},
|
||||
|
|
@ -367,7 +369,7 @@ static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
|
|||
pa_cli_command_sink_inputs(c, t, buf, fail);
|
||||
pa_cli_command_source_outputs(c, t, buf, fail);
|
||||
pa_cli_command_scache_list(c, t, buf, fail);
|
||||
pa_cli_command_autoload_list(c, t, buf, fail);
|
||||
/* pa_cli_command_autoload_list(c, t, buf, fail); */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -419,6 +421,45 @@ static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int pa_cli_command_describe(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
|
||||
const char *name;
|
||||
pa_modinfo *i;
|
||||
|
||||
pa_core_assert_ref(c);
|
||||
pa_assert(t);
|
||||
pa_assert(buf);
|
||||
pa_assert(fail);
|
||||
|
||||
if (!(name = pa_tokenizer_get(t, 1))) {
|
||||
pa_strbuf_puts(buf, "You need to specify the module name.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((i = pa_modinfo_get_by_name(name))) {
|
||||
|
||||
pa_strbuf_printf(buf, "Name: %s\n", name);
|
||||
|
||||
if (!i->description && !i->version && !i->author && !i->usage)
|
||||
pa_strbuf_printf(buf, "No module information available\n");
|
||||
else {
|
||||
if (i->version)
|
||||
pa_strbuf_printf(buf, "Version: %s\n", i->version);
|
||||
if (i->description)
|
||||
pa_strbuf_printf(buf, "Description: %s\n", i->description);
|
||||
if (i->author)
|
||||
pa_strbuf_printf(buf, "Author: %s\n", i->author);
|
||||
if (i->usage)
|
||||
pa_strbuf_printf(buf, "Usage: %s\n", i->usage);
|
||||
pa_strbuf_printf(buf, "Load Once: %s\n", pa_yes_no(i->load_once));
|
||||
}
|
||||
|
||||
pa_modinfo_free(i);
|
||||
} else
|
||||
pa_strbuf_puts(buf, "Failed to open module.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
|
||||
const char *n, *v;
|
||||
pa_sink *sink;
|
||||
|
|
@ -436,7 +477,7 @@ static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
|
|||
}
|
||||
|
||||
if (!(v = pa_tokenizer_get(t, 2))) {
|
||||
pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n");
|
||||
pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x10000 is normal volume)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -478,7 +519,7 @@ static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strb
|
|||
}
|
||||
|
||||
if (!(v = pa_tokenizer_get(t, 2))) {
|
||||
pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n");
|
||||
pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x10000 is normal volume)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -514,7 +555,7 @@ static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *
|
|||
}
|
||||
|
||||
if (!(v = pa_tokenizer_get(t, 2))) {
|
||||
pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n");
|
||||
pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x10000 is normal volume)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -553,7 +594,7 @@ static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf,
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (pa_atoi(m, &mute) < 0) {
|
||||
if ((mute = pa_parse_boolean(m)) < 0) {
|
||||
pa_strbuf_puts(buf, "Failed to parse mute switch.\n");
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -587,7 +628,7 @@ static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (pa_atoi(m, &mute) < 0) {
|
||||
if ((mute = pa_parse_boolean(m)) < 0) {
|
||||
pa_strbuf_puts(buf, "Failed to parse mute switch.\n");
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -623,11 +664,11 @@ static int pa_cli_command_sink_input_mute(pa_core *c, pa_tokenizer *t, pa_strbuf
|
|||
}
|
||||
|
||||
if (!(v = pa_tokenizer_get(t, 2))) {
|
||||
pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n");
|
||||
pa_strbuf_puts(buf, "You need to specify a mute switch setting (0/1).\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pa_atoi(v, &mute) < 0) {
|
||||
if ((mute = pa_parse_boolean(v)) < 0) {
|
||||
pa_strbuf_puts(buf, "Failed to parse mute switch.\n");
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -780,6 +821,7 @@ static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
|
|||
static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
|
||||
const char *n, *sink_name;
|
||||
pa_sink *sink;
|
||||
uint32_t idx;
|
||||
|
||||
pa_core_assert_ref(c);
|
||||
pa_assert(t);
|
||||
|
|
@ -796,11 +838,13 @@ static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (pa_scache_play_item(c, n, sink, PA_VOLUME_NORM) < 0) {
|
||||
if (pa_scache_play_item(c, n, sink, PA_VOLUME_NORM, NULL, &idx) < 0) {
|
||||
pa_strbuf_puts(buf, "Failed to play sample.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
pa_strbuf_printf(buf, "Playing on sink input #%i\n", idx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -902,6 +946,8 @@ static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *b
|
|||
pa_assert(buf);
|
||||
pa_assert(fail);
|
||||
|
||||
pa_log_warn("Autoload will no longer be implemented by future versions of the PulseAudio server.");
|
||||
|
||||
if (!(a = pa_tokenizer_get(t, 1)) || !(b = pa_tokenizer_get(t, 2))) {
|
||||
pa_strbuf_puts(buf, "You need to specify a device name, a filename or a module name and optionally module arguments\n");
|
||||
return -1;
|
||||
|
|
@ -920,6 +966,8 @@ static int pa_cli_command_autoload_remove(pa_core *c, pa_tokenizer *t, pa_strbuf
|
|||
pa_assert(buf);
|
||||
pa_assert(fail);
|
||||
|
||||
pa_log_warn("Autoload will no longer be implemented by future versions of the PulseAudio server.");
|
||||
|
||||
if (!(name = pa_tokenizer_get(t, 1))) {
|
||||
pa_strbuf_puts(buf, "You need to specify a device name\n");
|
||||
return -1;
|
||||
|
|
@ -941,6 +989,8 @@ static int pa_cli_command_autoload_list(pa_core *c, pa_tokenizer *t, pa_strbuf *
|
|||
pa_assert(buf);
|
||||
pa_assert(fail);
|
||||
|
||||
pa_log_warn("Autoload will no longer be implemented by future versions of the PulseAudio server.");
|
||||
|
||||
pa_assert_se(s = pa_autoload_list_to_string(c));
|
||||
pa_strbuf_puts(buf, s);
|
||||
pa_xfree(s);
|
||||
|
|
@ -1005,7 +1055,7 @@ static int pa_cli_command_move_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (pa_sink_input_move_to(si, sink, 0) < 0) {
|
||||
if (pa_sink_input_move_to(si, sink) < 0) {
|
||||
pa_strbuf_puts(buf, "Moved failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -1075,7 +1125,7 @@ static int pa_cli_command_suspend_sink(pa_core *c, pa_tokenizer *t, pa_strbuf *b
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (pa_atoi(m, &suspend) < 0) {
|
||||
if ((suspend = pa_parse_boolean(m)) < 0) {
|
||||
pa_strbuf_puts(buf, "Failed to parse suspend switch.\n");
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -1109,7 +1159,7 @@ static int pa_cli_command_suspend_source(pa_core *c, pa_tokenizer *t, pa_strbuf
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (pa_atoi(m, &suspend) < 0) {
|
||||
if ((suspend = pa_parse_boolean(m)) < 0) {
|
||||
pa_strbuf_puts(buf, "Failed to parse suspend switch.\n");
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -1138,7 +1188,7 @@ static int pa_cli_command_suspend(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, p
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (pa_atoi(m, &suspend) < 0) {
|
||||
if ((suspend = pa_parse_boolean(m)) < 0) {
|
||||
pa_strbuf_puts(buf, "Failed to parse suspend switch.\n");
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -1202,7 +1252,8 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
|
|||
}
|
||||
|
||||
pa_strbuf_printf(buf, "set-sink-volume %s 0x%03x\n", sink->name, pa_cvolume_avg(pa_sink_get_volume(sink)));
|
||||
pa_strbuf_printf(buf, "set-sink-mute %s %d\n", sink->name, pa_sink_get_mute(sink));
|
||||
pa_strbuf_printf(buf, "set-sink-mute %s %s\n", sink->name, pa_yes_no(pa_sink_get_mute(sink)));
|
||||
pa_strbuf_printf(buf, "suspend-sink %s %s\n", sink->name, pa_yes_no(pa_sink_get_state(sink) == PA_SINK_SUSPENDED));
|
||||
}
|
||||
|
||||
for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx)) {
|
||||
|
|
@ -1215,7 +1266,8 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
|
|||
}
|
||||
|
||||
pa_strbuf_printf(buf, "set-source-volume %s 0x%03x\n", source->name, pa_cvolume_avg(pa_source_get_volume(source)));
|
||||
pa_strbuf_printf(buf, "set-source-mute %s %d\n", source->name, pa_source_get_mute(source));
|
||||
pa_strbuf_printf(buf, "set-source-mute %s %s\n", source->name, pa_yes_no(pa_source_get_mute(source)));
|
||||
pa_strbuf_printf(buf, "suspend-source %s %s\n", source->name, pa_yes_no(pa_source_get_state(source) == PA_SOURCE_SUSPENDED));
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1390,16 +1442,45 @@ int pa_cli_command_execute_line(pa_core *c, const char *s, pa_strbuf *buf, pa_bo
|
|||
return pa_cli_command_execute_line_stateful(c, s, buf, fail, NULL);
|
||||
}
|
||||
|
||||
int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, pa_bool_t *fail) {
|
||||
int pa_cli_command_execute_file_stream(pa_core *c, FILE *f, pa_strbuf *buf, pa_bool_t *fail) {
|
||||
char line[1024];
|
||||
FILE *f = NULL;
|
||||
int ifstate = IFSTATE_NONE;
|
||||
int ret = -1;
|
||||
pa_bool_t _fail = TRUE;
|
||||
|
||||
pa_assert(c);
|
||||
pa_assert(f);
|
||||
pa_assert(buf);
|
||||
|
||||
if (!fail)
|
||||
fail = &_fail;
|
||||
|
||||
while (fgets(line, sizeof(line), f)) {
|
||||
pa_strip_nl(line);
|
||||
|
||||
if (pa_cli_command_execute_line_stateful(c, line, buf, fail, &ifstate) < 0 && *fail)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
fail:
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, pa_bool_t *fail) {
|
||||
FILE *f = NULL;
|
||||
int ret = -1;
|
||||
pa_bool_t _fail = TRUE;
|
||||
|
||||
pa_assert(c);
|
||||
pa_assert(fn);
|
||||
pa_assert(buf);
|
||||
|
||||
if (!fail)
|
||||
fail = &_fail;
|
||||
|
||||
if (!(f = fopen(fn, "r"))) {
|
||||
pa_strbuf_printf(buf, "open('%s') failed: %s\n", fn, pa_cstrerror(errno));
|
||||
if (!*fail)
|
||||
|
|
@ -1407,13 +1488,7 @@ int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, pa_b
|
|||
goto fail;
|
||||
}
|
||||
|
||||
while (fgets(line, sizeof(line), f)) {
|
||||
char *e = line + strcspn(line, linebreak);
|
||||
*e = 0;
|
||||
|
||||
if (pa_cli_command_execute_line_stateful(c, line, buf, fail, &ifstate) < 0 && *fail)
|
||||
goto fail;
|
||||
}
|
||||
ret = pa_cli_command_execute_file_stream(c, f, buf, fail);
|
||||
|
||||
ret = 0;
|
||||
|
||||
|
|
@ -1427,11 +1502,15 @@ fail:
|
|||
int pa_cli_command_execute(pa_core *c, const char *s, pa_strbuf *buf, pa_bool_t *fail) {
|
||||
const char *p;
|
||||
int ifstate = IFSTATE_NONE;
|
||||
pa_bool_t _fail = TRUE;
|
||||
|
||||
pa_assert(c);
|
||||
pa_assert(s);
|
||||
pa_assert(buf);
|
||||
|
||||
if (!fail)
|
||||
fail = &_fail;
|
||||
|
||||
p = s;
|
||||
while (*p) {
|
||||
size_t l = strcspn(p, linebreak);
|
||||
|
|
|
|||
|
|
@ -36,6 +36,9 @@ int pa_cli_command_execute_line(pa_core *c, const char *s, pa_strbuf *buf, pa_bo
|
|||
/* Execute a whole file of CLI commands */
|
||||
int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, pa_bool_t *fail);
|
||||
|
||||
/* Execute a whole file of CLI commands */
|
||||
int pa_cli_command_execute_file_stream(pa_core *c, FILE *f, pa_strbuf *buf, pa_bool_t *fail);
|
||||
|
||||
/* Split the specified string into lines and run pa_cli_command_execute_line() for each. */
|
||||
int pa_cli_command_execute(pa_core *c, const char *s, pa_strbuf *buf, pa_bool_t *fail);
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
|
||||
#include <pulse/volume.h>
|
||||
#include <pulse/xmalloc.h>
|
||||
#include <pulse/timeval.h>
|
||||
|
||||
#include <pulsecore/module.h>
|
||||
#include <pulsecore/client.h>
|
||||
|
|
@ -41,6 +42,7 @@
|
|||
#include <pulsecore/core-scache.h>
|
||||
#include <pulsecore/autoload.h>
|
||||
#include <pulsecore/macro.h>
|
||||
#include <pulsecore/core-util.h>
|
||||
|
||||
#include "cli-text.h"
|
||||
|
||||
|
|
@ -56,12 +58,12 @@ char *pa_module_list_to_string(pa_core *c) {
|
|||
|
||||
for (m = pa_idxset_first(c->modules, &idx); m; m = pa_idxset_next(c->modules, &idx)) {
|
||||
pa_strbuf_printf(s, " index: %u\n"
|
||||
"\tname: <%s>\n"
|
||||
"\targument: <%s>\n"
|
||||
"\tused: %i\n"
|
||||
"\tauto unload: %s\n",
|
||||
m->index, m->name, m->argument ? m->argument : "", m->n_used,
|
||||
m->auto_unload ? "yes" : "no");
|
||||
"\tname: <%s>\n"
|
||||
"\targument: <%s>\n"
|
||||
"\tused: %i\n"
|
||||
"\tauto unload: %s\n",
|
||||
m->index, m->name, m->argument ? m->argument : "", m->n_used,
|
||||
pa_yes_no(m->auto_unload));
|
||||
}
|
||||
|
||||
return pa_strbuf_tostring_free(s);
|
||||
|
|
@ -78,10 +80,20 @@ char *pa_client_list_to_string(pa_core *c) {
|
|||
pa_strbuf_printf(s, "%u client(s) logged in.\n", pa_idxset_size(c->clients));
|
||||
|
||||
for (client = pa_idxset_first(c->clients, &idx); client; client = pa_idxset_next(c->clients, &idx)) {
|
||||
pa_strbuf_printf(s, " index: %u\n\tname: <%s>\n\tdriver: <%s>\n", client->index, client->name, client->driver);
|
||||
char *t;
|
||||
pa_strbuf_printf(
|
||||
s,
|
||||
" index: %u\n"
|
||||
"\tdriver: <%s>\n",
|
||||
client->index,
|
||||
client->driver);
|
||||
|
||||
if (client->owner)
|
||||
pa_strbuf_printf(s, "\towner module: <%u>\n", client->owner->index);
|
||||
if (client->module)
|
||||
pa_strbuf_printf(s, "\towner module: %u\n", client->module->index);
|
||||
|
||||
t = pa_proplist_to_string(client->proplist);
|
||||
pa_strbuf_printf(s, "\tproperties:\n%s", t);
|
||||
pa_xfree(t);
|
||||
}
|
||||
|
||||
return pa_strbuf_tostring_free(s);
|
||||
|
|
@ -92,6 +104,7 @@ char *pa_sink_list_to_string(pa_core *c) {
|
|||
pa_sink *sink;
|
||||
uint32_t idx = PA_IDXSET_INVALID;
|
||||
static const char* const state_table[] = {
|
||||
[PA_SINK_INIT] = "INIT",
|
||||
[PA_SINK_RUNNING] = "RUNNING",
|
||||
[PA_SINK_SUSPENDED] = "SUSPENDED",
|
||||
[PA_SINK_IDLE] = "IDLE",
|
||||
|
|
@ -104,35 +117,39 @@ char *pa_sink_list_to_string(pa_core *c) {
|
|||
pa_strbuf_printf(s, "%u sink(s) available.\n", pa_idxset_size(c->sinks));
|
||||
|
||||
for (sink = pa_idxset_first(c->sinks, &idx); sink; sink = pa_idxset_next(c->sinks, &idx)) {
|
||||
char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
|
||||
char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t;
|
||||
|
||||
pa_strbuf_printf(
|
||||
s,
|
||||
" %c index: %u\n"
|
||||
"\tname: <%s>\n"
|
||||
"\tdriver: <%s>\n"
|
||||
"\tflags: %s%s%s%s\n"
|
||||
"\tflags: %s%s%s%s%s%s\n"
|
||||
"\tstate: %s\n"
|
||||
"\tvolume: <%s>\n"
|
||||
"\tmute: <%i>\n"
|
||||
"\tlatency: <%0.0f usec>\n"
|
||||
"\tmonitor source: <%u>\n"
|
||||
"\tsample spec: <%s>\n"
|
||||
"\tchannel map: <%s>\n"
|
||||
"\tused by: <%u>\n"
|
||||
"\tlinked by: <%u>\n",
|
||||
"\tvolume: %s\n"
|
||||
"\tmuted: %s\n"
|
||||
"\tcurrent latency: %0.2f ms\n"
|
||||
"\tconfigured latency: %0.2f ms; range is %0.2f .. %0.2f ms\n"
|
||||
"\tmonitor source: %u\n"
|
||||
"\tsample spec: %s\n"
|
||||
"\tchannel map: %s\n"
|
||||
"\tused by: %u\n"
|
||||
"\tlinked by: %u\n",
|
||||
c->default_sink_name && !strcmp(sink->name, c->default_sink_name) ? '*' : ' ',
|
||||
sink->index,
|
||||
sink->name,
|
||||
sink->driver,
|
||||
sink->flags & PA_SINK_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
|
||||
sink->flags & PA_SINK_LATENCY ? "LATENCY " : "",
|
||||
sink->flags & PA_SINK_HARDWARE ? "HARDWARE " : "",
|
||||
sink->flags & PA_SINK_NETWORK ? "NETWORK " : "",
|
||||
sink->flags & PA_SINK_HW_MUTE_CTRL ? "HW_MUTE_CTRL " : "",
|
||||
sink->flags & PA_SINK_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
|
||||
sink->flags & PA_SINK_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
|
||||
sink->flags & PA_SINK_LATENCY ? "LATENCY " : "",
|
||||
state_table[pa_sink_get_state(sink)],
|
||||
pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink)),
|
||||
!!pa_sink_get_mute(sink),
|
||||
(double) pa_sink_get_latency(sink),
|
||||
pa_yes_no(pa_sink_get_mute(sink)),
|
||||
(double) pa_sink_get_latency(sink) / PA_USEC_PER_MSEC,
|
||||
(double) pa_sink_get_requested_latency(sink) / PA_USEC_PER_MSEC, (double) sink->min_latency / PA_USEC_PER_MSEC, (double) sink->max_latency / PA_USEC_PER_MSEC,
|
||||
sink->monitor_source ? sink->monitor_source->index : PA_INVALID_INDEX,
|
||||
pa_sample_spec_snprint(ss, sizeof(ss), &sink->sample_spec),
|
||||
pa_channel_map_snprint(cm, sizeof(cm), &sink->channel_map),
|
||||
|
|
@ -140,9 +157,11 @@ char *pa_sink_list_to_string(pa_core *c) {
|
|||
pa_sink_linked_by(sink));
|
||||
|
||||
if (sink->module)
|
||||
pa_strbuf_printf(s, "\tmodule: <%u>\n", sink->module->index);
|
||||
if (sink->description)
|
||||
pa_strbuf_printf(s, "\tdescription: <%s>\n", sink->description);
|
||||
pa_strbuf_printf(s, "\tmodule: %u\n", sink->module->index);
|
||||
|
||||
t = pa_proplist_to_string(sink->proplist);
|
||||
pa_strbuf_printf(s, "\tproperties:\n%s", t);
|
||||
pa_xfree(t);
|
||||
}
|
||||
|
||||
return pa_strbuf_tostring_free(s);
|
||||
|
|
@ -153,6 +172,7 @@ char *pa_source_list_to_string(pa_core *c) {
|
|||
pa_source *source;
|
||||
uint32_t idx = PA_IDXSET_INVALID;
|
||||
static const char* const state_table[] = {
|
||||
[PA_SOURCE_INIT] = "INIT",
|
||||
[PA_SOURCE_RUNNING] = "RUNNING",
|
||||
[PA_SOURCE_SUSPENDED] = "SUSPENDED",
|
||||
[PA_SOURCE_IDLE] = "IDLE",
|
||||
|
|
@ -165,46 +185,51 @@ char *pa_source_list_to_string(pa_core *c) {
|
|||
pa_strbuf_printf(s, "%u source(s) available.\n", pa_idxset_size(c->sources));
|
||||
|
||||
for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx)) {
|
||||
char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX];
|
||||
|
||||
char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], *t;
|
||||
|
||||
pa_strbuf_printf(
|
||||
s,
|
||||
" %c index: %u\n"
|
||||
"\tname: <%s>\n"
|
||||
"\tdriver: <%s>\n"
|
||||
"\tflags: %s%s%s%s\n"
|
||||
"\tflags: %s%s%s%s%s%s\n"
|
||||
"\tstate: %s\n"
|
||||
"\tvolume: <%s>\n"
|
||||
"\tmute: <%u>\n"
|
||||
"\tlatency: <%0.0f usec>\n"
|
||||
"\tsample spec: <%s>\n"
|
||||
"\tchannel map: <%s>\n"
|
||||
"\tused by: <%u>\n"
|
||||
"\tlinked by: <%u>\n",
|
||||
"\tvolume: %s\n"
|
||||
"\tmuted: %s\n"
|
||||
"\tcurrent latency: %0.2f ms\n"
|
||||
"\tconfigured latency: %0.2f ms; range is %0.2f .. %0.2f ms\n"
|
||||
"\tsample spec: %s\n"
|
||||
"\tchannel map: %s\n"
|
||||
"\tused by: %u\n"
|
||||
"\tlinked by: %u\n",
|
||||
c->default_source_name && !strcmp(source->name, c->default_source_name) ? '*' : ' ',
|
||||
source->index,
|
||||
source->name,
|
||||
source->driver,
|
||||
source->flags & PA_SOURCE_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
|
||||
source->flags & PA_SOURCE_LATENCY ? "LATENCY " : "",
|
||||
source->flags & PA_SOURCE_HARDWARE ? "HARDWARE " : "",
|
||||
source->flags & PA_SOURCE_NETWORK ? "NETWORK " : "",
|
||||
source->flags & PA_SOURCE_HW_MUTE_CTRL ? "HW_MUTE_CTRL " : "",
|
||||
source->flags & PA_SOURCE_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
|
||||
source->flags & PA_SOURCE_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
|
||||
source->flags & PA_SOURCE_LATENCY ? "LATENCY " : "",
|
||||
state_table[pa_source_get_state(source)],
|
||||
pa_cvolume_snprint(cv, sizeof(cv), pa_source_get_volume(source)),
|
||||
!!pa_source_get_mute(source),
|
||||
(double) pa_source_get_latency(source),
|
||||
pa_yes_no(pa_source_get_mute(source)),
|
||||
(double) pa_source_get_latency(source) / PA_USEC_PER_MSEC,
|
||||
(double) pa_source_get_requested_latency(source) / PA_USEC_PER_MSEC, (double) source->min_latency / PA_USEC_PER_MSEC, (double) source->max_latency / PA_USEC_PER_MSEC,
|
||||
pa_sample_spec_snprint(ss, sizeof(ss), &source->sample_spec),
|
||||
pa_channel_map_snprint(cm, sizeof(cm), &source->channel_map),
|
||||
pa_source_used_by(source),
|
||||
pa_source_linked_by(source));
|
||||
|
||||
if (source->monitor_of)
|
||||
pa_strbuf_printf(s, "\tmonitor_of: <%u>\n", source->monitor_of->index);
|
||||
pa_strbuf_printf(s, "\tmonitor_of: %u\n", source->monitor_of->index);
|
||||
if (source->module)
|
||||
pa_strbuf_printf(s, "\tmodule: <%u>\n", source->module->index);
|
||||
if (source->description)
|
||||
pa_strbuf_printf(s, "\tdescription: <%s>\n", source->description);
|
||||
pa_strbuf_printf(s, "\tmodule: %u\n", source->module->index);
|
||||
|
||||
t = pa_proplist_to_string(source->proplist);
|
||||
pa_strbuf_printf(s, "\tproperties:\n%s", t);
|
||||
pa_xfree(t);
|
||||
}
|
||||
|
||||
return pa_strbuf_tostring_free(s);
|
||||
|
|
@ -216,6 +241,7 @@ char *pa_source_output_list_to_string(pa_core *c) {
|
|||
pa_source_output *o;
|
||||
uint32_t idx = PA_IDXSET_INVALID;
|
||||
static const char* const state_table[] = {
|
||||
[PA_SOURCE_OUTPUT_INIT] = "INIT",
|
||||
[PA_SOURCE_OUTPUT_RUNNING] = "RUNNING",
|
||||
[PA_SOURCE_OUTPUT_CORKED] = "CORKED",
|
||||
[PA_SOURCE_OUTPUT_UNLINKED] = "UNLINKED"
|
||||
|
|
@ -227,27 +253,33 @@ char *pa_source_output_list_to_string(pa_core *c) {
|
|||
pa_strbuf_printf(s, "%u source outputs(s) available.\n", pa_idxset_size(c->source_outputs));
|
||||
|
||||
for (o = pa_idxset_first(c->source_outputs, &idx); o; o = pa_idxset_next(c->source_outputs, &idx)) {
|
||||
char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
|
||||
char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t, clt[28];
|
||||
pa_usec_t cl;
|
||||
|
||||
if ((cl = pa_source_output_get_requested_latency(o)) == (pa_usec_t) -1)
|
||||
pa_snprintf(clt, sizeof(clt), "n/a");
|
||||
else
|
||||
pa_snprintf(clt, sizeof(clt), "%0.2f ms", (double) cl / PA_USEC_PER_MSEC);
|
||||
|
||||
pa_assert(o->source);
|
||||
|
||||
pa_strbuf_printf(
|
||||
s,
|
||||
" index: %u\n"
|
||||
"\tname: '%s'\n"
|
||||
"\tdriver: <%s>\n"
|
||||
"\tflags: %s%s%s%s%s%s%s\n"
|
||||
"\tflags: %s%s%s%s%s%s%s%s\n"
|
||||
"\tstate: %s\n"
|
||||
"\tsource: <%u> '%s'\n"
|
||||
"\tlatency: <%0.0f usec>\n"
|
||||
"\tsample spec: <%s>\n"
|
||||
"\tchannel map: <%s>\n"
|
||||
"\tsource: %u <%s>\n"
|
||||
"\tcurrent latency: %0.2f ms\n"
|
||||
"\trequested latency: %s\n"
|
||||
"\tsample spec: %s\n"
|
||||
"\tchannel map: %s\n"
|
||||
"\tresample method: %s\n",
|
||||
o->index,
|
||||
o->name,
|
||||
o->driver,
|
||||
o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE ? "VARIABLE_RATE " : "",
|
||||
o->flags & PA_SOURCE_OUTPUT_DONT_MOVE ? "DONT_MOVE " : "",
|
||||
o->flags & PA_SOURCE_OUTPUT_START_CORKED ? "START_CORKED " : "",
|
||||
o->flags & PA_SOURCE_OUTPUT_NO_REMAP ? "NO_REMAP " : "",
|
||||
o->flags & PA_SOURCE_OUTPUT_NO_REMIX ? "NO_REMIX " : "",
|
||||
o->flags & PA_SOURCE_OUTPUT_FIX_FORMAT ? "FIX_FORMAT " : "",
|
||||
|
|
@ -255,14 +287,19 @@ char *pa_source_output_list_to_string(pa_core *c) {
|
|||
o->flags & PA_SOURCE_OUTPUT_FIX_CHANNELS ? "FIX_CHANNELS " : "",
|
||||
state_table[pa_source_output_get_state(o)],
|
||||
o->source->index, o->source->name,
|
||||
(double) pa_source_output_get_latency(o),
|
||||
(double) pa_source_output_get_latency(o) / PA_USEC_PER_MSEC,
|
||||
clt,
|
||||
pa_sample_spec_snprint(ss, sizeof(ss), &o->sample_spec),
|
||||
pa_channel_map_snprint(cm, sizeof(cm), &o->channel_map),
|
||||
pa_resample_method_to_string(pa_source_output_get_resample_method(o)));
|
||||
if (o->module)
|
||||
pa_strbuf_printf(s, "\towner module: <%u>\n", o->module->index);
|
||||
pa_strbuf_printf(s, "\towner module: %u\n", o->module->index);
|
||||
if (o->client)
|
||||
pa_strbuf_printf(s, "\tclient: <%u> '%s'\n", o->client->index, o->client->name);
|
||||
pa_strbuf_printf(s, "\tclient: %u <%s>\n", o->client->index, pa_strnull(pa_proplist_gets(o->client->proplist, PA_PROP_APPLICATION_NAME)));
|
||||
|
||||
t = pa_proplist_to_string(o->proplist);
|
||||
pa_strbuf_printf(s, "\tproperties:\n%s", t);
|
||||
pa_xfree(t);
|
||||
}
|
||||
|
||||
return pa_strbuf_tostring_free(s);
|
||||
|
|
@ -273,6 +310,7 @@ char *pa_sink_input_list_to_string(pa_core *c) {
|
|||
pa_sink_input *i;
|
||||
uint32_t idx = PA_IDXSET_INVALID;
|
||||
static const char* const state_table[] = {
|
||||
[PA_SINK_INPUT_INIT] = "INIT",
|
||||
[PA_SINK_INPUT_RUNNING] = "RUNNING",
|
||||
[PA_SINK_INPUT_DRAINED] = "DRAINED",
|
||||
[PA_SINK_INPUT_CORKED] = "CORKED",
|
||||
|
|
@ -285,29 +323,35 @@ char *pa_sink_input_list_to_string(pa_core *c) {
|
|||
pa_strbuf_printf(s, "%u sink input(s) available.\n", pa_idxset_size(c->sink_inputs));
|
||||
|
||||
for (i = pa_idxset_first(c->sink_inputs, &idx); i; i = pa_idxset_next(c->sink_inputs, &idx)) {
|
||||
char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
|
||||
char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t, clt[28];
|
||||
pa_usec_t cl;
|
||||
|
||||
if ((cl = pa_sink_input_get_requested_latency(i)) == (pa_usec_t) -1)
|
||||
pa_snprintf(clt, sizeof(clt), "n/a");
|
||||
else
|
||||
pa_snprintf(clt, sizeof(clt), "%0.2f ms", (double) cl / PA_USEC_PER_MSEC);
|
||||
|
||||
pa_assert(i->sink);
|
||||
|
||||
pa_strbuf_printf(
|
||||
s,
|
||||
" index: %u\n"
|
||||
"\tname: <%s>\n"
|
||||
"\tdriver: <%s>\n"
|
||||
"\tflags: %s%s%s%s%s%s%s\n"
|
||||
"\tflags: %s%s%s%s%s%s%s%s\n"
|
||||
"\tstate: %s\n"
|
||||
"\tsink: <%u> '%s'\n"
|
||||
"\tvolume: <%s>\n"
|
||||
"\tmute: <%i>\n"
|
||||
"\tlatency: <%0.0f usec>\n"
|
||||
"\tsample spec: <%s>\n"
|
||||
"\tchannel map: <%s>\n"
|
||||
"\tsink: %u <%s>\n"
|
||||
"\tvolume: %s\n"
|
||||
"\tmuted: %s\n"
|
||||
"\tcurrent latency: %0.2f ms\n"
|
||||
"\trequested latency: %s\n"
|
||||
"\tsample spec: %s\n"
|
||||
"\tchannel map: %s\n"
|
||||
"\tresample method: %s\n",
|
||||
i->index,
|
||||
i->name,
|
||||
i->driver,
|
||||
i->flags & PA_SINK_INPUT_VARIABLE_RATE ? "VARIABLE_RATE " : "",
|
||||
i->flags & PA_SINK_INPUT_DONT_MOVE ? "DONT_MOVE " : "",
|
||||
i->flags & PA_SINK_INPUT_START_CORKED ? "START_CORKED " : "",
|
||||
i->flags & PA_SINK_INPUT_NO_REMAP ? "NO_REMAP " : "",
|
||||
i->flags & PA_SINK_INPUT_NO_REMIX ? "NO_REMIX " : "",
|
||||
i->flags & PA_SINK_INPUT_FIX_FORMAT ? "FIX_FORMAT " : "",
|
||||
|
|
@ -316,16 +360,21 @@ char *pa_sink_input_list_to_string(pa_core *c) {
|
|||
state_table[pa_sink_input_get_state(i)],
|
||||
i->sink->index, i->sink->name,
|
||||
pa_cvolume_snprint(cv, sizeof(cv), pa_sink_input_get_volume(i)),
|
||||
!!pa_sink_input_get_mute(i),
|
||||
(double) pa_sink_input_get_latency(i),
|
||||
pa_yes_no(pa_sink_input_get_mute(i)),
|
||||
(double) pa_sink_input_get_latency(i) / PA_USEC_PER_MSEC,
|
||||
clt,
|
||||
pa_sample_spec_snprint(ss, sizeof(ss), &i->sample_spec),
|
||||
pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
|
||||
pa_resample_method_to_string(pa_sink_input_get_resample_method(i)));
|
||||
|
||||
if (i->module)
|
||||
pa_strbuf_printf(s, "\tmodule: <%u>\n", i->module->index);
|
||||
pa_strbuf_printf(s, "\tmodule: %u\n", i->module->index);
|
||||
if (i->client)
|
||||
pa_strbuf_printf(s, "\tclient: <%u> '%s'\n", i->client->index, i->client->name);
|
||||
pa_strbuf_printf(s, "\tclient: %u <%s>\n", i->client->index, pa_strnull(pa_proplist_gets(i->client->proplist, PA_PROP_APPLICATION_NAME)));
|
||||
|
||||
t = pa_proplist_to_string(i->proplist);
|
||||
pa_strbuf_printf(s, "\tproperties:\n%s", t);
|
||||
pa_xfree(t);
|
||||
}
|
||||
|
||||
return pa_strbuf_tostring_free(s);
|
||||
|
|
@ -345,7 +394,7 @@ char *pa_scache_list_to_string(pa_core *c) {
|
|||
|
||||
for (e = pa_idxset_first(c->scache, &idx); e; e = pa_idxset_next(c->scache, &idx)) {
|
||||
double l = 0;
|
||||
char ss[PA_SAMPLE_SPEC_SNPRINT_MAX] = "n/a", cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX] = "n/a";
|
||||
char ss[PA_SAMPLE_SPEC_SNPRINT_MAX] = "n/a", cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX] = "n/a", *t;
|
||||
|
||||
if (e->memchunk.memblock) {
|
||||
pa_sample_spec_snprint(ss, sizeof(ss), &e->sample_spec);
|
||||
|
|
@ -356,14 +405,14 @@ char *pa_scache_list_to_string(pa_core *c) {
|
|||
pa_strbuf_printf(
|
||||
s,
|
||||
" name: <%s>\n"
|
||||
"\tindex: <%u>\n"
|
||||
"\tsample spec: <%s>\n"
|
||||
"\tchannel map: <%s>\n"
|
||||
"\tlength: <%lu>\n"
|
||||
"\tduration: <%0.1fs>\n"
|
||||
"\tvolume: <%s>\n"
|
||||
"\tindex: %u\n"
|
||||
"\tsample spec: %s\n"
|
||||
"\tchannel map: %s\n"
|
||||
"\tlength: %lu\n"
|
||||
"\tduration: %0.1f s\n"
|
||||
"\tvolume: %s\n"
|
||||
"\tlazy: %s\n"
|
||||
"\tfilename: %s\n",
|
||||
"\tfilename: <%s>\n",
|
||||
e->name,
|
||||
e->index,
|
||||
ss,
|
||||
|
|
@ -371,8 +420,12 @@ char *pa_scache_list_to_string(pa_core *c) {
|
|||
(long unsigned)(e->memchunk.memblock ? e->memchunk.length : 0),
|
||||
l,
|
||||
pa_cvolume_snprint(cv, sizeof(cv), &e->volume),
|
||||
e->lazy ? "yes" : "no",
|
||||
pa_yes_no(e->lazy),
|
||||
e->filename ? e->filename : "n/a");
|
||||
|
||||
t = pa_proplist_to_string(e->proplist);
|
||||
pa_strbuf_printf(s, "\tproperties:\n%s", t);
|
||||
pa_xfree(t);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -393,7 +446,12 @@ char *pa_autoload_list_to_string(pa_core *c) {
|
|||
|
||||
while ((e = pa_hashmap_iterate(c->autoload_hashmap, &state, NULL))) {
|
||||
pa_strbuf_printf(
|
||||
s, " name: <%s>\n\ttype: <%s>\n\tindex: <%u>\n\tmodule_name: <%s>\n\targuments: <%s>\n",
|
||||
s,
|
||||
" name: <%s>\n"
|
||||
"\ttype: %s\n"
|
||||
"\tindex: %u\n"
|
||||
"\tmodule_name: <%s>\n"
|
||||
"\targuments: <%s>\n",
|
||||
e->name,
|
||||
e->type == PA_NAMEREG_SOURCE ? "source" : "sink",
|
||||
e->index,
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ pa_cli* pa_cli_new(pa_core *core, pa_iochannel *io, pa_module *m) {
|
|||
pa_assert_se(c->client = pa_client_new(core, __FILE__, cname));
|
||||
c->client->kill = client_kill;
|
||||
c->client->userdata = c;
|
||||
c->client->owner = m;
|
||||
c->client->module = m;
|
||||
|
||||
pa_ioline_set_callback(c->line, line_callback, c);
|
||||
pa_ioline_puts(c->line, "Welcome to PulseAudio! Use \"help\" for usage information.\n"PROMPT);
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@
|
|||
#include <pulsecore/core-subscribe.h>
|
||||
#include <pulsecore/log.h>
|
||||
#include <pulsecore/macro.h>
|
||||
#include <pulsecore/core-util.h>
|
||||
|
||||
#include "client.h"
|
||||
|
||||
|
|
@ -44,17 +45,19 @@ pa_client *pa_client_new(pa_core *core, const char *driver, const char *name) {
|
|||
pa_core_assert_ref(core);
|
||||
|
||||
c = pa_xnew(pa_client, 1);
|
||||
c->name = pa_xstrdup(name);
|
||||
c->driver = pa_xstrdup(driver);
|
||||
c->owner = NULL;
|
||||
c->core = core;
|
||||
c->proplist = pa_proplist_new();
|
||||
if (name)
|
||||
pa_proplist_sets(c->proplist, PA_PROP_APPLICATION_NAME, name);
|
||||
c->driver = pa_xstrdup(driver);
|
||||
c->module = NULL;
|
||||
|
||||
c->kill = NULL;
|
||||
c->userdata = NULL;
|
||||
|
||||
pa_assert_se(pa_idxset_put(core->clients, c, &c->index) >= 0);
|
||||
|
||||
pa_log_info("Created %u \"%s\"", c->index, c->name);
|
||||
pa_log_info("Created %u \"%s\"", c->index, pa_strnull(name));
|
||||
pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_NEW, c->index);
|
||||
|
||||
pa_core_check_quit(core);
|
||||
|
|
@ -70,9 +73,9 @@ void pa_client_free(pa_client *c) {
|
|||
|
||||
pa_core_check_quit(c->core);
|
||||
|
||||
pa_log_info("Freed %u \"%s\"", c->index, c->name);
|
||||
pa_log_info("Freed %u \"%s\"", c->index, pa_strnull(pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME)));
|
||||
pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_REMOVE, c->index);
|
||||
pa_xfree(c->name);
|
||||
pa_proplist_free(c->proplist);
|
||||
pa_xfree(c->driver);
|
||||
pa_xfree(c);
|
||||
}
|
||||
|
|
@ -91,10 +94,7 @@ void pa_client_kill(pa_client *c) {
|
|||
void pa_client_set_name(pa_client *c, const char *name) {
|
||||
pa_assert(c);
|
||||
|
||||
pa_log_info("Client %u changed name from \"%s\" to \"%s\"", c->index, c->name, name);
|
||||
|
||||
pa_xfree(c->name);
|
||||
c->name = pa_xstrdup(name);
|
||||
|
||||
pa_log_info("Client %u changed name from \"%s\" to \"%s\"", c->index, pa_strnull(pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME)), name);
|
||||
pa_proplist_sets(c->proplist, PA_PROP_APPLICATION_NAME, name);
|
||||
pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->index);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
|
||||
typedef struct pa_client pa_client;
|
||||
|
||||
#include <pulse/proplist.h>
|
||||
#include <pulsecore/core.h>
|
||||
#include <pulsecore/module.h>
|
||||
|
||||
|
|
@ -37,11 +38,12 @@ typedef struct pa_client pa_client;
|
|||
|
||||
struct pa_client {
|
||||
uint32_t index;
|
||||
|
||||
pa_module *owner;
|
||||
char *name, *driver;
|
||||
pa_core *core;
|
||||
|
||||
pa_proplist *proplist;
|
||||
pa_module *module;
|
||||
char *driver;
|
||||
|
||||
void (*kill)(pa_client *c);
|
||||
void *userdata;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,29 +0,0 @@
|
|||
#ifndef foocoredefhfoo
|
||||
#define foocoredefhfoo
|
||||
|
||||
/* $Id$ */
|
||||
|
||||
/***
|
||||
This file is part of PulseAudio.
|
||||
|
||||
Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
|
||||
|
||||
PulseAudio is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published
|
||||
by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
PulseAudio is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with PulseAudio; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
USA.
|
||||
***/
|
||||
|
||||
/* FIXME: Remove this shit */
|
||||
|
||||
#endif
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
/***
|
||||
This file is part of PulseAudio.
|
||||
|
||||
Copyright 2004-2006 Lennart Poettering
|
||||
Copyright 2004-2008 Lennart Poettering
|
||||
Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
|
||||
|
||||
PulseAudio is free software; you can redistribute it and/or modify
|
||||
|
|
@ -63,7 +63,7 @@
|
|||
|
||||
#include "core-scache.h"
|
||||
|
||||
#define UNLOAD_POLL_TIME 2
|
||||
#define UNLOAD_POLL_TIME 5
|
||||
|
||||
static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
|
||||
pa_core *c = userdata;
|
||||
|
|
@ -89,6 +89,8 @@ static void free_entry(pa_scache_entry *e) {
|
|||
pa_xfree(e->filename);
|
||||
if (e->memchunk.memblock)
|
||||
pa_memblock_unref(e->memchunk.memblock);
|
||||
if (e->proplist)
|
||||
pa_proplist_free(e->proplist);
|
||||
pa_xfree(e);
|
||||
}
|
||||
|
||||
|
|
@ -103,6 +105,7 @@ static pa_scache_entry* scache_add_item(pa_core *c, const char *name) {
|
|||
pa_memblock_unref(e->memchunk.memblock);
|
||||
|
||||
pa_xfree(e->filename);
|
||||
pa_proplist_clear(e->proplist);
|
||||
|
||||
pa_assert(e->core == c);
|
||||
|
||||
|
|
@ -117,11 +120,10 @@ static pa_scache_entry* scache_add_item(pa_core *c, const char *name) {
|
|||
|
||||
e->name = pa_xstrdup(name);
|
||||
e->core = c;
|
||||
e->proplist = pa_proplist_new();
|
||||
|
||||
if (!c->scache) {
|
||||
if (!c->scache)
|
||||
c->scache = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
|
||||
pa_assert(c->scache);
|
||||
}
|
||||
|
||||
pa_idxset_put(c->scache, e, &e->index);
|
||||
|
||||
|
|
@ -132,17 +134,27 @@ static pa_scache_entry* scache_add_item(pa_core *c, const char *name) {
|
|||
e->memchunk.memblock = NULL;
|
||||
e->memchunk.index = e->memchunk.length = 0;
|
||||
e->filename = NULL;
|
||||
e->lazy = 0;
|
||||
e->lazy = FALSE;
|
||||
e->last_used_time = 0;
|
||||
|
||||
memset(&e->sample_spec, 0, sizeof(e->sample_spec));
|
||||
pa_channel_map_init(&e->channel_map);
|
||||
pa_cvolume_reset(&e->volume, PA_CHANNELS_MAX);
|
||||
|
||||
pa_proplist_sets(e->proplist, PA_PROP_MEDIA_ROLE, "event");
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
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, uint32_t *idx) {
|
||||
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;
|
||||
|
|
@ -178,6 +190,9 @@ int pa_scache_add_item(pa_core *c, const char *name, const pa_sample_spec *ss, c
|
|||
pa_memblock_ref(e->memchunk.memblock);
|
||||
}
|
||||
|
||||
if (p)
|
||||
pa_proplist_update(e->proplist, PA_UPDATE_REPLACE, p);
|
||||
|
||||
if (idx)
|
||||
*idx = e->index;
|
||||
|
||||
|
|
@ -193,6 +208,7 @@ int pa_scache_add_file(pa_core *c, const char *name, const char *filename, uint3
|
|||
pa_channel_map map;
|
||||
pa_memchunk chunk;
|
||||
int r;
|
||||
pa_proplist *p;
|
||||
|
||||
#ifdef OS_IS_WIN32
|
||||
char buf[MAX_PATH];
|
||||
|
|
@ -208,8 +224,11 @@ int pa_scache_add_file(pa_core *c, const char *name, const char *filename, uint3
|
|||
if (pa_sound_file_load(c->mempool, filename, &ss, &map, &chunk) < 0)
|
||||
return -1;
|
||||
|
||||
r = pa_scache_add_item(c, name, &ss, &map, &chunk, idx);
|
||||
p = pa_proplist_new();
|
||||
pa_proplist_sets(p, PA_PROP_MEDIA_FILENAME, filename);
|
||||
r = pa_scache_add_item(c, name, &ss, &map, &chunk, p, idx);
|
||||
pa_memblock_unref(chunk.memblock);
|
||||
pa_proplist_free(p);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
|
@ -231,9 +250,11 @@ int pa_scache_add_file_lazy(pa_core *c, const char *name, const char *filename,
|
|||
if (!(e = scache_add_item(c, name)))
|
||||
return -1;
|
||||
|
||||
e->lazy = 1;
|
||||
e->lazy = TRUE;
|
||||
e->filename = pa_xstrdup(filename);
|
||||
|
||||
pa_proplist_sets(e->proplist, PA_PROP_MEDIA_FILENAME, filename);
|
||||
|
||||
if (!c->scache_auto_unload_event) {
|
||||
struct timeval ntv;
|
||||
pa_gettimeofday(&ntv);
|
||||
|
|
@ -285,10 +306,10 @@ void pa_scache_free(pa_core *c) {
|
|||
c->mainloop->time_free(c->scache_auto_unload_event);
|
||||
}
|
||||
|
||||
int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t volume) {
|
||||
int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx) {
|
||||
pa_scache_entry *e;
|
||||
char *t;
|
||||
pa_cvolume r;
|
||||
pa_proplist *merged;
|
||||
|
||||
pa_assert(c);
|
||||
pa_assert(name);
|
||||
|
|
@ -312,17 +333,24 @@ int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t
|
|||
|
||||
pa_log_debug("Playing sample \"%s\" on \"%s\"", name, sink->name);
|
||||
|
||||
t = pa_sprintf_malloc("sample:%s", name);
|
||||
|
||||
pa_cvolume_set(&r, e->volume.channels, volume);
|
||||
pa_sw_cvolume_multiply(&r, &r, &e->volume);
|
||||
|
||||
if (pa_play_memchunk(sink, t, &e->sample_spec, &e->channel_map, &e->memchunk, &r) < 0) {
|
||||
pa_xfree(t);
|
||||
merged = pa_proplist_new();
|
||||
|
||||
pa_proplist_setf(merged, PA_PROP_MEDIA_NAME, "Sample %s", name);
|
||||
|
||||
pa_proplist_update(merged, PA_UPDATE_REPLACE, e->proplist);
|
||||
|
||||
if (p)
|
||||
pa_proplist_update(merged, PA_UPDATE_REPLACE, p);
|
||||
|
||||
if (pa_play_memchunk(sink, &e->sample_spec, &e->channel_map, &e->memchunk, &r, merged, sink_input_idx) < 0) {
|
||||
pa_proplist_free(merged);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pa_xfree(t);
|
||||
pa_proplist_free(merged);
|
||||
|
||||
if (e->lazy)
|
||||
time(&e->last_used_time);
|
||||
|
|
@ -330,7 +358,7 @@ int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t
|
|||
return 0;
|
||||
}
|
||||
|
||||
int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_name, pa_volume_t volume, int autoload) {
|
||||
int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_name, pa_bool_t autoload, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx) {
|
||||
pa_sink *sink;
|
||||
|
||||
pa_assert(c);
|
||||
|
|
@ -339,10 +367,10 @@ int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_na
|
|||
if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK, autoload)))
|
||||
return -1;
|
||||
|
||||
return pa_scache_play_item(c, name, sink, volume);
|
||||
return pa_scache_play_item(c, name, sink, volume, p, sink_input_idx);
|
||||
}
|
||||
|
||||
const char * pa_scache_get_name_by_id(pa_core *c, uint32_t id) {
|
||||
const char *pa_scache_get_name_by_id(pa_core *c, uint32_t id) {
|
||||
pa_scache_entry *e;
|
||||
|
||||
pa_assert(c);
|
||||
|
|
@ -366,9 +394,10 @@ uint32_t pa_scache_get_id_by_name(pa_core *c, const char *name) {
|
|||
return e->index;
|
||||
}
|
||||
|
||||
uint32_t pa_scache_total_size(pa_core *c) {
|
||||
size_t pa_scache_total_size(pa_core *c) {
|
||||
pa_scache_entry *e;
|
||||
uint32_t idx, sum = 0;
|
||||
uint32_t idx;
|
||||
size_t sum = 0;
|
||||
|
||||
pa_assert(c);
|
||||
|
||||
|
|
@ -403,8 +432,7 @@ void pa_scache_unload_unused(pa_core *c) {
|
|||
continue;
|
||||
|
||||
pa_memblock_unref(e->memchunk.memblock);
|
||||
e->memchunk.memblock = NULL;
|
||||
e->memchunk.index = e->memchunk.length = 0;
|
||||
pa_memchunk_reset(&e->memchunk);
|
||||
|
||||
pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index);
|
||||
}
|
||||
|
|
@ -467,8 +495,9 @@ int pa_scache_add_directory_lazy(pa_core *c, const char *pathname) {
|
|||
pa_snprintf(p, sizeof(p), "%s/%s", pathname, e->d_name);
|
||||
add_file(c, p);
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,11 +29,12 @@
|
|||
#include <pulsecore/memchunk.h>
|
||||
#include <pulsecore/sink.h>
|
||||
|
||||
#define PA_SCACHE_ENTRY_SIZE_MAX (1024*1024*2)
|
||||
#define PA_SCACHE_ENTRY_SIZE_MAX (1024*1024*16)
|
||||
|
||||
typedef struct pa_scache_entry {
|
||||
pa_core *core;
|
||||
uint32_t index;
|
||||
pa_core *core;
|
||||
|
||||
char *name;
|
||||
|
||||
pa_cvolume volume;
|
||||
|
|
@ -43,25 +44,27 @@ typedef struct pa_scache_entry {
|
|||
|
||||
char *filename;
|
||||
|
||||
int lazy;
|
||||
pa_bool_t lazy;
|
||||
time_t last_used_time;
|
||||
|
||||
pa_proplist *proplist;
|
||||
} pa_scache_entry;
|
||||
|
||||
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, uint32_t *idx);
|
||||
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);
|
||||
int pa_scache_add_file(pa_core *c, const char *name, const char *filename, uint32_t *idx);
|
||||
int pa_scache_add_file_lazy(pa_core *c, const char *name, const char *filename, uint32_t *idx);
|
||||
|
||||
int pa_scache_add_directory_lazy(pa_core *c, const char *pathname);
|
||||
|
||||
int pa_scache_remove_item(pa_core *c, const char *name);
|
||||
int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t volume);
|
||||
int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_name, pa_volume_t volume, int autoload);
|
||||
int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx);
|
||||
int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_name, pa_bool_t autoload, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx);
|
||||
void pa_scache_free(pa_core *c);
|
||||
|
||||
const char *pa_scache_get_name_by_id(pa_core *c, uint32_t id);
|
||||
uint32_t pa_scache_get_id_by_name(pa_core *c, const char *name);
|
||||
|
||||
uint32_t pa_scache_total_size(pa_core *c);
|
||||
size_t pa_scache_total_size(pa_core *c);
|
||||
|
||||
void pa_scache_unload_unused(pa_core *c);
|
||||
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@
|
|||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#ifdef HAVE_STRTOF_L
|
||||
#include <locale.h>
|
||||
|
|
@ -103,12 +104,6 @@
|
|||
#define MSG_NOSIGNAL 0
|
||||
#endif
|
||||
|
||||
#ifndef OS_IS_WIN32
|
||||
#define PA_USER_RUNTIME_PATH_PREFIX "/tmp/pulse-"
|
||||
#else
|
||||
#define PA_USER_RUNTIME_PATH_PREFIX "%TEMP%\\pulse-"
|
||||
#endif
|
||||
|
||||
#ifdef OS_IS_WIN32
|
||||
|
||||
#define PULSE_ROOTENV "PULSE_ROOT"
|
||||
|
|
@ -221,7 +216,7 @@ int pa_make_secure_dir(const char* dir, mode_t m, uid_t uid, gid_t gid) {
|
|||
goto fail;
|
||||
}
|
||||
#else
|
||||
pa_log_warn("secure directory creation not supported on Win32.");
|
||||
pa_log_warn("Secure directory creation not supported on Win32.");
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
|
@ -557,6 +552,82 @@ int pa_make_realtime(int rtprio) {
|
|||
#endif
|
||||
}
|
||||
|
||||
/* This is merely used for giving the user a hint. This is not correct
|
||||
* for anything security related */
|
||||
pa_bool_t pa_can_realtime(void) {
|
||||
|
||||
if (geteuid() == 0)
|
||||
return TRUE;
|
||||
|
||||
#if defined(HAVE_SYS_RESOURCE_H) && defined(RLIMIT_RTPRIO)
|
||||
{
|
||||
struct rlimit rl;
|
||||
|
||||
if (getrlimit(RLIMIT_RTPRIO, &rl) >= 0)
|
||||
if (rl.rlim_cur > 0 || rl.rlim_cur == RLIM_INFINITY)
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_SYS_CAPABILITY_H) && defined(CAP_SYS_NICE)
|
||||
{
|
||||
cap_t cap;
|
||||
|
||||
if ((cap = cap_get_proc())) {
|
||||
cap_flag_value_t flag = CAP_CLEAR;
|
||||
|
||||
if (cap_get_flag(cap, CAP_SYS_NICE, CAP_EFFECTIVE, &flag) >= 0)
|
||||
if (flag == CAP_SET) {
|
||||
cap_free(cap);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
cap_free(cap);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* This is merely used for giving the user a hint. This is not correct
|
||||
* for anything security related */
|
||||
pa_bool_t pa_can_high_priority(void) {
|
||||
|
||||
if (geteuid() == 0)
|
||||
return TRUE;
|
||||
|
||||
#if defined(HAVE_SYS_RESOURCE_H) && defined(RLIMIT_RTPRIO)
|
||||
{
|
||||
struct rlimit rl;
|
||||
|
||||
if (getrlimit(RLIMIT_NICE, &rl) >= 0)
|
||||
if (rl.rlim_cur >= 21 || rl.rlim_cur == RLIM_INFINITY)
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_SYS_CAPABILITY_H) && defined(CAP_SYS_NICE)
|
||||
{
|
||||
cap_t cap;
|
||||
|
||||
if ((cap = cap_get_proc())) {
|
||||
cap_flag_value_t flag = CAP_CLEAR;
|
||||
|
||||
if (cap_get_flag(cap, CAP_SYS_NICE, CAP_EFFECTIVE, &flag) >= 0)
|
||||
if (flag == CAP_SET) {
|
||||
cap_free(cap);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
cap_free(cap);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Raise the priority of the current process as much as possible that
|
||||
* is <= the specified nice level..*/
|
||||
int pa_raise_priority(int nice_level) {
|
||||
|
|
@ -612,6 +683,7 @@ void pa_reset_priority(void) {
|
|||
|
||||
/* Try to parse a boolean string value.*/
|
||||
int pa_parse_boolean(const char *v) {
|
||||
pa_assert(v);
|
||||
|
||||
if (!strcmp(v, "1") || v[0] == 'y' || v[0] == 'Y' || v[0] == 't' || v[0] == 'T' || !strcasecmp(v, "on"))
|
||||
return 1;
|
||||
|
|
@ -1093,16 +1165,126 @@ int pa_unlock_lockfile(const char *fn, int fd) {
|
|||
return r;
|
||||
}
|
||||
|
||||
char *pa_get_runtime_dir(void) {
|
||||
const char *e;
|
||||
char *d;
|
||||
|
||||
if ((e = getenv("PULSE_RUNTIME_PATH")))
|
||||
d = pa_xstrdup(e);
|
||||
else {
|
||||
char h[PATH_MAX];
|
||||
|
||||
if (!pa_get_home_dir(h, sizeof(h))) {
|
||||
pa_log_error("Failed to get home directory.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
d = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse", h);
|
||||
}
|
||||
|
||||
if (pa_make_secure_dir(d, 0700, (pid_t) -1, (pid_t) -1) < 0) {
|
||||
pa_log_error("Failed to create secure directory: %s", pa_cstrerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
/* Try to open a configuration file. If "env" is specified, open the
|
||||
* value of the specified environment variable. Otherwise look for a
|
||||
* file "local" in the home directory or a file "global" in global
|
||||
* file system. If "result" is non-NULL, a pointer to a newly
|
||||
* allocated buffer containing the used configuration file is
|
||||
* stored there.*/
|
||||
FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result, const char *mode) {
|
||||
FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result) {
|
||||
const char *fn;
|
||||
char h[PATH_MAX];
|
||||
#ifdef OS_IS_WIN32
|
||||
char buf[PATH_MAX];
|
||||
|
||||
if (!getenv(PULSE_ROOTENV))
|
||||
pa_set_root(NULL);
|
||||
#endif
|
||||
|
||||
if (env && (fn = getenv(env))) {
|
||||
FILE *f;
|
||||
|
||||
#ifdef OS_IS_WIN32
|
||||
if (!ExpandEnvironmentStrings(fn, buf, PATH_MAX))
|
||||
return NULL;
|
||||
fn = buf;
|
||||
#endif
|
||||
|
||||
if ((f = fopen(fn, "r"))) {
|
||||
if (result)
|
||||
*result = pa_xstrdup(fn);
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
pa_log_warn("Failed to open configuration file '%s': %s", fn, pa_cstrerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (local) {
|
||||
const char *e;
|
||||
char *lfn;
|
||||
char h[PATH_MAX];
|
||||
FILE *f;
|
||||
|
||||
if ((e = getenv("PULSE_CONFIG_PATH")))
|
||||
fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", e, local);
|
||||
else if (pa_get_home_dir(h, sizeof(h)))
|
||||
fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse" PA_PATH_SEP "%s", h, local);
|
||||
|
||||
#ifdef OS_IS_WIN32
|
||||
if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX)) {
|
||||
pa_xfree(lfn);
|
||||
return NULL;
|
||||
}
|
||||
fn = buf;
|
||||
#endif
|
||||
|
||||
if ((f = fopen(fn, "r"))) {
|
||||
if (result)
|
||||
*result = pa_xstrdup(fn);
|
||||
|
||||
pa_xfree(lfn);
|
||||
return f;
|
||||
}
|
||||
|
||||
if (errno != ENOENT) {
|
||||
pa_log_warn("Failed to open configuration file '%s': %s", fn, pa_cstrerror(errno));
|
||||
pa_xfree(lfn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pa_xfree(lfn);
|
||||
}
|
||||
|
||||
if (global) {
|
||||
FILE *f;
|
||||
|
||||
#ifdef OS_IS_WIN32
|
||||
if (!ExpandEnvironmentStrings(global, buf, PATH_MAX))
|
||||
return NULL;
|
||||
global = buf;
|
||||
#endif
|
||||
|
||||
if ((f = fopen(global, "r"))) {
|
||||
|
||||
if (result)
|
||||
*result = pa_xstrdup(global);
|
||||
|
||||
return f;
|
||||
}
|
||||
} else
|
||||
errno = ENOENT;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *pa_find_config_file(const char *global, const char *local, const char *env) {
|
||||
const char *fn;
|
||||
#ifdef OS_IS_WIN32
|
||||
char buf[PATH_MAX];
|
||||
|
||||
|
|
@ -1117,69 +1299,59 @@ FILE *pa_open_config_file(const char *global, const char *local, const char *env
|
|||
fn = buf;
|
||||
#endif
|
||||
|
||||
if (result)
|
||||
*result = pa_xstrdup(fn);
|
||||
if (access(fn, R_OK) == 0)
|
||||
return pa_xstrdup(fn);
|
||||
|
||||
return fopen(fn, mode);
|
||||
pa_log_warn("Failed to access configuration file '%s': %s", fn, pa_cstrerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (local) {
|
||||
const char *e;
|
||||
char *lfn = NULL;
|
||||
char *lfn;
|
||||
char h[PATH_MAX];
|
||||
|
||||
if ((e = getenv("PULSE_CONFIG_PATH")))
|
||||
fn = lfn = pa_sprintf_malloc("%s/%s", e, local);
|
||||
else if (pa_get_home_dir(h, sizeof(h))) {
|
||||
char *d;
|
||||
|
||||
d = pa_sprintf_malloc("%s/.pulse", h);
|
||||
mkdir(d, 0755);
|
||||
pa_xfree(d);
|
||||
|
||||
fn = lfn = pa_sprintf_malloc("%s/.pulse/%s", h, local);
|
||||
}
|
||||
|
||||
if (lfn) {
|
||||
FILE *f;
|
||||
fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", e, local);
|
||||
else if (pa_get_home_dir(h, sizeof(h)))
|
||||
fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse" PA_PATH_SEP "%s", h, local);
|
||||
|
||||
#ifdef OS_IS_WIN32
|
||||
if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX))
|
||||
return NULL;
|
||||
fn = buf;
|
||||
#endif
|
||||
|
||||
f = fopen(fn, mode);
|
||||
if (f != NULL) {
|
||||
if (result)
|
||||
*result = pa_xstrdup(fn);
|
||||
pa_xfree(lfn);
|
||||
return f;
|
||||
}
|
||||
|
||||
if (errno != ENOENT)
|
||||
pa_log_warn("Failed to open configuration file '%s': %s", lfn, pa_cstrerror(errno));
|
||||
|
||||
if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX)) {
|
||||
pa_xfree(lfn);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!global) {
|
||||
if (result)
|
||||
*result = NULL;
|
||||
errno = ENOENT;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef OS_IS_WIN32
|
||||
if (!ExpandEnvironmentStrings(global, buf, PATH_MAX))
|
||||
return NULL;
|
||||
global = buf;
|
||||
fn = buf;
|
||||
#endif
|
||||
|
||||
if (result)
|
||||
*result = pa_xstrdup(global);
|
||||
if (access(fn, R_OK) == 0) {
|
||||
char *r = pa_xstrdup(fn);
|
||||
pa_xfree(lfn);
|
||||
return r;
|
||||
}
|
||||
|
||||
return fopen(global, mode);
|
||||
if (errno != ENOENT) {
|
||||
pa_log_warn("Failed to access configuration file '%s': %s", fn, pa_cstrerror(errno));
|
||||
pa_xfree(lfn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pa_xfree(lfn);
|
||||
}
|
||||
|
||||
if (global) {
|
||||
#ifdef OS_IS_WIN32
|
||||
if (!ExpandEnvironmentStrings(global, buf, PATH_MAX))
|
||||
return NULL;
|
||||
global = buf;
|
||||
#endif
|
||||
|
||||
if (access(fn, R_OK) == 0)
|
||||
return pa_xstrdup(global);
|
||||
} else
|
||||
errno = ENOENT;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Format the specified data as a hexademical string */
|
||||
|
|
@ -1270,45 +1442,51 @@ int pa_endswith(const char *s, const char *sfx) {
|
|||
return l1 >= l2 && strcmp(s+l1-l2, sfx) == 0;
|
||||
}
|
||||
|
||||
/* if fn is null return the PulseAudio run time path in s (/tmp/pulse)
|
||||
* if fn is non-null and starts with / return fn in s
|
||||
* otherwise append fn to the run time path and return it in s */
|
||||
char *pa_runtime_path(const char *fn, char *s, size_t l) {
|
||||
const char *e;
|
||||
pa_bool_t pa_is_path_absolute(const char *fn) {
|
||||
pa_assert(fn);
|
||||
|
||||
#ifndef OS_IS_WIN32
|
||||
if (fn && *fn == '/')
|
||||
return *fn == '/';
|
||||
#else
|
||||
if (fn && strlen(fn) >= 3 && isalpha(fn[0]) && fn[1] == ':' && fn[2] == '\\')
|
||||
return strlen(fn) >= 3 && isalpha(fn[0]) && fn[1] == ':' && fn[2] == '\\';
|
||||
#endif
|
||||
return pa_strlcpy(s, fn, l);
|
||||
}
|
||||
|
||||
if ((e = getenv("PULSE_RUNTIME_PATH"))) {
|
||||
char *pa_make_path_absolute(const char *p) {
|
||||
char *r;
|
||||
char *cwd;
|
||||
|
||||
if (fn)
|
||||
pa_snprintf(s, l, "%s%c%s", e, PA_PATH_SEP_CHAR, fn);
|
||||
else
|
||||
pa_snprintf(s, l, "%s", e);
|
||||
pa_assert(p);
|
||||
|
||||
} else {
|
||||
char u[256];
|
||||
if (pa_is_path_absolute(p))
|
||||
return pa_xstrdup(p);
|
||||
|
||||
if (fn)
|
||||
pa_snprintf(s, l, "%s%s%c%s", PA_USER_RUNTIME_PATH_PREFIX, pa_get_user_name(u, sizeof(u)), PA_PATH_SEP_CHAR, fn);
|
||||
else
|
||||
pa_snprintf(s, l, "%s%s", PA_USER_RUNTIME_PATH_PREFIX, pa_get_user_name(u, sizeof(u)));
|
||||
}
|
||||
if (!(cwd = pa_getcwd()))
|
||||
return pa_xstrdup(p);
|
||||
|
||||
r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", cwd, p);
|
||||
pa_xfree(cwd);
|
||||
return r;
|
||||
}
|
||||
|
||||
#ifdef OS_IS_WIN32
|
||||
{
|
||||
char buf[l];
|
||||
strcpy(buf, s);
|
||||
ExpandEnvironmentStrings(buf, s, l);
|
||||
}
|
||||
#endif
|
||||
/* if fn is null return the PulseAudio run time path in s (~/.pulse)
|
||||
* if fn is non-null and starts with / return fn
|
||||
* otherwise append fn to the run time path and return it */
|
||||
char *pa_runtime_path(const char *fn) {
|
||||
char *rtp;
|
||||
|
||||
return s;
|
||||
if (pa_is_path_absolute(fn))
|
||||
return pa_xstrdup(fn);
|
||||
|
||||
rtp = pa_get_runtime_dir();
|
||||
|
||||
if (fn) {
|
||||
char *r;
|
||||
r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", rtp, fn);
|
||||
pa_xfree(rtp);
|
||||
return r;
|
||||
} else
|
||||
return rtp;
|
||||
}
|
||||
|
||||
/* Convert the string s to a signed integer in *ret_i */
|
||||
|
|
@ -1414,12 +1592,28 @@ int pa_snprintf(char *str, size_t size, const char *format, ...) {
|
|||
pa_assert(format);
|
||||
|
||||
va_start(ap, format);
|
||||
ret = vsnprintf(str, size, format, ap);
|
||||
ret = pa_vsnprintf(str, size, format, ap);
|
||||
va_end(ap);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Same as vsnprintf, but guarantees NUL-termination on every platform */
|
||||
int pa_vsnprintf(char *str, size_t size, const char *format, va_list ap) {
|
||||
int ret;
|
||||
|
||||
pa_assert(str);
|
||||
pa_assert(size > 0);
|
||||
pa_assert(format);
|
||||
|
||||
ret = vsnprintf(str, size, format, ap);
|
||||
|
||||
str[size-1] = 0;
|
||||
|
||||
return ret;
|
||||
if (ret < 0)
|
||||
ret = strlen(str);
|
||||
|
||||
return PA_MIN((int) size-1, ret);
|
||||
}
|
||||
|
||||
/* Truncate the specified string, but guarantee that the string
|
||||
|
|
@ -1455,23 +1649,6 @@ char *pa_getcwd(void) {
|
|||
}
|
||||
}
|
||||
|
||||
char *pa_make_path_absolute(const char *p) {
|
||||
char *r;
|
||||
char *cwd;
|
||||
|
||||
pa_assert(p);
|
||||
|
||||
if (p[0] == '/')
|
||||
return pa_xstrdup(p);
|
||||
|
||||
if (!(cwd = pa_getcwd()))
|
||||
return pa_xstrdup(p);
|
||||
|
||||
r = pa_sprintf_malloc("%s/%s", cwd, p);
|
||||
pa_xfree(cwd);
|
||||
return r;
|
||||
}
|
||||
|
||||
void *pa_will_need(const void *p, size_t l) {
|
||||
#ifdef RLIMIT_MEMLOCK
|
||||
struct rlimit rlim;
|
||||
|
|
@ -1577,3 +1754,249 @@ char *pa_readlink(const char *p) {
|
|||
l *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
int pa_close_all(int except_fd, ...) {
|
||||
va_list ap;
|
||||
int n = 0, i, r;
|
||||
int *p;
|
||||
|
||||
va_start(ap, except_fd);
|
||||
|
||||
if (except_fd >= 0)
|
||||
for (n = 1; va_arg(ap, int) >= 0; n++)
|
||||
;
|
||||
|
||||
va_end(ap);
|
||||
|
||||
p = pa_xnew(int, n+1);
|
||||
|
||||
va_start(ap, except_fd);
|
||||
|
||||
i = 0;
|
||||
if (except_fd >= 0) {
|
||||
p[i++] = except_fd;
|
||||
|
||||
while ((p[i++] = va_arg(ap, int)) >= 0)
|
||||
;
|
||||
}
|
||||
p[i] = -1;
|
||||
|
||||
va_end(ap);
|
||||
|
||||
r = pa_close_allv(p);
|
||||
free(p);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int pa_close_allv(const int except_fds[]) {
|
||||
struct rlimit rl;
|
||||
int fd;
|
||||
int saved_errno;
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
DIR *d;
|
||||
|
||||
if ((d = opendir("/proc/self/fd"))) {
|
||||
|
||||
struct dirent *de;
|
||||
|
||||
while ((de = readdir(d))) {
|
||||
long l;
|
||||
char *e = NULL;
|
||||
int i;
|
||||
|
||||
if (de->d_name[0] == '.')
|
||||
continue;
|
||||
|
||||
errno = 0;
|
||||
l = strtol(de->d_name, &e, 10);
|
||||
if (errno != 0 || !e || *e) {
|
||||
closedir(d);
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
fd = (int) l;
|
||||
|
||||
if ((long) fd != l) {
|
||||
closedir(d);
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fd <= 3)
|
||||
continue;
|
||||
|
||||
if (fd == dirfd(d))
|
||||
continue;
|
||||
|
||||
for (i = 0; except_fds[i] >= 0; i++)
|
||||
if (except_fds[i] == fd)
|
||||
continue;
|
||||
|
||||
if (close(fd) < 0) {
|
||||
saved_errno = errno;
|
||||
closedir(d);
|
||||
errno = saved_errno;
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
closedir(d);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if (getrlimit(RLIMIT_NOFILE, &rl) < 0)
|
||||
return -1;
|
||||
|
||||
for (fd = 0; fd < (int) rl.rlim_max; fd++) {
|
||||
int i;
|
||||
|
||||
if (fd <= 3)
|
||||
continue;
|
||||
|
||||
for (i = 0; except_fds[i] >= 0; i++)
|
||||
if (except_fds[i] == fd)
|
||||
continue;
|
||||
|
||||
if (close(fd) < 0 && errno != EBADF)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pa_unblock_sigs(int except, ...) {
|
||||
va_list ap;
|
||||
int n = 0, i, r;
|
||||
int *p;
|
||||
|
||||
va_start(ap, except);
|
||||
|
||||
if (except >= 1)
|
||||
for (n = 1; va_arg(ap, int) >= 0; n++)
|
||||
;
|
||||
|
||||
va_end(ap);
|
||||
|
||||
p = pa_xnew(int, n+1);
|
||||
|
||||
va_start(ap, except);
|
||||
|
||||
i = 0;
|
||||
if (except >= 1) {
|
||||
p[i++] = except;
|
||||
|
||||
while ((p[i++] = va_arg(ap, int)) >= 0)
|
||||
;
|
||||
}
|
||||
p[i] = -1;
|
||||
|
||||
va_end(ap);
|
||||
|
||||
r = pa_unblock_sigsv(p);
|
||||
pa_xfree(p);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int pa_unblock_sigsv(const int except[]) {
|
||||
int i;
|
||||
sigset_t ss;
|
||||
|
||||
if (sigemptyset(&ss) < 0)
|
||||
return -1;
|
||||
|
||||
for (i = 0; except[i] > 0; i++)
|
||||
if (sigaddset(&ss, except[i]) < 0)
|
||||
return -1;
|
||||
|
||||
return sigprocmask(SIG_SETMASK, &ss, NULL);
|
||||
}
|
||||
|
||||
int pa_reset_sigs(int except, ...) {
|
||||
va_list ap;
|
||||
int n = 0, i, r;
|
||||
int *p;
|
||||
|
||||
va_start(ap, except);
|
||||
|
||||
if (except >= 1)
|
||||
for (n = 1; va_arg(ap, int) >= 0; n++)
|
||||
;
|
||||
|
||||
va_end(ap);
|
||||
|
||||
p = pa_xnew(int, n+1);
|
||||
|
||||
va_start(ap, except);
|
||||
|
||||
i = 0;
|
||||
if (except >= 1) {
|
||||
p[i++] = except;
|
||||
|
||||
while ((p[i++] = va_arg(ap, int)) >= 0)
|
||||
;
|
||||
}
|
||||
p[i] = -1;
|
||||
|
||||
va_end(ap);
|
||||
|
||||
r = pa_reset_sigsv(p);
|
||||
pa_xfree(p);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int pa_reset_sigsv(const int except[]) {
|
||||
int sig;
|
||||
|
||||
for (sig = 1; sig < _NSIG; sig++) {
|
||||
int reset = 1;
|
||||
|
||||
switch (sig) {
|
||||
case SIGKILL:
|
||||
case SIGSTOP:
|
||||
reset = 0;
|
||||
break;
|
||||
|
||||
default: {
|
||||
int i;
|
||||
|
||||
for (i = 0; except[i] > 0; i++) {
|
||||
if (sig == except[i]) {
|
||||
reset = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (reset) {
|
||||
struct sigaction sa;
|
||||
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sa_handler = SIG_DFL;
|
||||
|
||||
/* On Linux the first two RT signals are reserved by
|
||||
* glibc, and sigaction() will return EINVAL for them. */
|
||||
if ((sigaction(sig, &sa, NULL) < 0))
|
||||
if (errno != EINVAL)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pa_set_env(const char *key, const char *value) {
|
||||
pa_assert(key);
|
||||
pa_assert(value);
|
||||
|
||||
putenv(pa_sprintf_malloc("%s=%s", key, value));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,11 +30,27 @@
|
|||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <pulsecore/gccmacro.h>
|
||||
#ifdef HAVE_SYS_RESOURCE_H
|
||||
#include <sys/resource.h>
|
||||
#endif
|
||||
|
||||
#include <pulse/gccmacro.h>
|
||||
#include <pulsecore/macro.h>
|
||||
|
||||
struct timeval;
|
||||
|
||||
/* These resource limits are pretty new on Linux, let's define them
|
||||
* here manually, in case the kernel is newer than the glibc */
|
||||
#if !defined(RLIMIT_NICE) && defined(__linux__)
|
||||
#define RLIMIT_NICE 13
|
||||
#endif
|
||||
#if !defined(RLIMIT_RTPRIO) && defined(__linux__)
|
||||
#define RLIMIT_RTPRIO 14
|
||||
#endif
|
||||
#if !defined(RLIMIT_RTTIME) && defined(__linux__)
|
||||
#define RLIMIT_RTTIME 15
|
||||
#endif
|
||||
|
||||
void pa_make_fd_nonblock(int fd);
|
||||
void pa_make_fd_cloexec(int fd);
|
||||
|
||||
|
|
@ -61,12 +77,23 @@ int pa_make_realtime(int rtprio);
|
|||
int pa_raise_priority(int nice_level);
|
||||
void pa_reset_priority(void);
|
||||
|
||||
pa_bool_t pa_can_realtime(void);
|
||||
pa_bool_t pa_can_high_priority(void);
|
||||
|
||||
int pa_parse_boolean(const char *s) PA_GCC_PURE;
|
||||
|
||||
static inline const char *pa_yes_no(pa_bool_t b) {
|
||||
return b ? "yes" : "no";
|
||||
}
|
||||
|
||||
static inline const char *pa_strnull(const char *x) {
|
||||
return x ? x : "(null)";
|
||||
}
|
||||
|
||||
static inline const char *pa_strempty(const char *x) {
|
||||
return x ? x : "";
|
||||
}
|
||||
|
||||
char *pa_split(const char *c, const char*delimiters, const char **state);
|
||||
char *pa_split_spaces(const char *c, const char **state);
|
||||
|
||||
|
|
@ -84,26 +111,30 @@ int pa_lock_fd(int fd, int b);
|
|||
int pa_lock_lockfile(const char *fn);
|
||||
int pa_unlock_lockfile(const char *fn, int fd);
|
||||
|
||||
FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result, const char *mode);
|
||||
|
||||
char *pa_hexstr(const uint8_t* d, size_t dlength, char *s, size_t slength);
|
||||
size_t pa_parsehex(const char *p, uint8_t *d, size_t dlength);
|
||||
|
||||
int pa_startswith(const char *s, const char *pfx) PA_GCC_PURE;
|
||||
int pa_endswith(const char *s, const char *sfx) PA_GCC_PURE;
|
||||
|
||||
char *pa_runtime_path(const char *fn, char *s, size_t l);
|
||||
FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result);
|
||||
char* pa_find_config_file(const char *global, const char *local, const char *env);
|
||||
|
||||
char *pa_get_runtime_dir(void);
|
||||
char *pa_runtime_path(const char *fn);
|
||||
|
||||
int pa_atoi(const char *s, int32_t *ret_i);
|
||||
int pa_atou(const char *s, uint32_t *ret_u);
|
||||
int pa_atof(const char *s, float *ret_f);
|
||||
|
||||
int pa_snprintf(char *str, size_t size, const char *format, ...);
|
||||
int pa_vsnprintf(char *str, size_t size, const char *format, va_list ap);
|
||||
|
||||
char *pa_truncate_utf8(char *c, size_t l);
|
||||
|
||||
char *pa_getcwd(void);
|
||||
char *pa_make_path_absolute(const char *p);
|
||||
pa_bool_t pa_is_path_absolute(const char *p);
|
||||
|
||||
void *pa_will_need(const void *p, size_t l);
|
||||
|
||||
|
|
@ -125,8 +156,28 @@ static inline unsigned pa_make_power_of_two(unsigned n) {
|
|||
return n + 1;
|
||||
}
|
||||
|
||||
static inline unsigned pa_ulog2(unsigned n) {
|
||||
unsigned r = 0;
|
||||
|
||||
while (n) {
|
||||
r++;
|
||||
n = n >> 1;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void pa_close_pipe(int fds[2]);
|
||||
|
||||
char *pa_readlink(const char *p);
|
||||
|
||||
int pa_close_all(int except_fd, ...);
|
||||
int pa_close_allv(const int except_fds[]);
|
||||
int pa_unblock_sigs(int except, ...);
|
||||
int pa_unblock_sigsv(const int except[]);
|
||||
int pa_reset_sigs(int except, ...);
|
||||
int pa_reset_sigsv(const int except[]);
|
||||
|
||||
void pa_set_env(const char *key, const char *value);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -125,6 +125,7 @@ pa_core* pa_core_new(pa_mainloop_api *m, int shared) {
|
|||
c->subscription_event_last = NULL;
|
||||
|
||||
c->mempool = pool;
|
||||
pa_silence_cache_init(&c->silence_cache);
|
||||
|
||||
c->quit_event = NULL;
|
||||
|
||||
|
|
@ -188,6 +189,7 @@ static void core_free(pa_object *o) {
|
|||
pa_xfree(c->default_source_name);
|
||||
pa_xfree(c->default_sink_name);
|
||||
|
||||
pa_silence_cache_done(&c->silence_cache);
|
||||
pa_mempool_free(c->mempool);
|
||||
|
||||
pa_property_cleanup(c);
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@
|
|||
#include <pulsecore/llist.h>
|
||||
#include <pulsecore/hook-list.h>
|
||||
#include <pulsecore/asyncmsgq.h>
|
||||
#include <pulsecore/sample-util.h>
|
||||
|
||||
typedef struct pa_core pa_core;
|
||||
|
||||
|
|
@ -43,16 +44,20 @@ typedef struct pa_core pa_core;
|
|||
#include <pulsecore/msgobject.h>
|
||||
|
||||
typedef enum pa_core_hook {
|
||||
PA_CORE_HOOK_SINK_NEW_POST,
|
||||
PA_CORE_HOOK_SINK_NEW,
|
||||
PA_CORE_HOOK_SINK_FIXATE,
|
||||
PA_CORE_HOOK_SINK_PUT,
|
||||
PA_CORE_HOOK_SINK_UNLINK,
|
||||
PA_CORE_HOOK_SINK_UNLINK_POST,
|
||||
PA_CORE_HOOK_SINK_STATE_CHANGED,
|
||||
PA_CORE_HOOK_SINK_DESCRIPTION_CHANGED,
|
||||
PA_CORE_HOOK_SOURCE_NEW_POST,
|
||||
PA_CORE_HOOK_SINK_PROPLIST_CHANGED,
|
||||
PA_CORE_HOOK_SOURCE_NEW,
|
||||
PA_CORE_HOOK_SOURCE_FIXATE,
|
||||
PA_CORE_HOOK_SOURCE_PUT,
|
||||
PA_CORE_HOOK_SOURCE_UNLINK,
|
||||
PA_CORE_HOOK_SOURCE_UNLINK_POST,
|
||||
PA_CORE_HOOK_SOURCE_STATE_CHANGED,
|
||||
PA_CORE_HOOK_SOURCE_DESCRIPTION_CHANGED,
|
||||
PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED,
|
||||
PA_CORE_HOOK_SINK_INPUT_NEW,
|
||||
PA_CORE_HOOK_SINK_INPUT_FIXATE,
|
||||
PA_CORE_HOOK_SINK_INPUT_PUT,
|
||||
|
|
@ -60,8 +65,8 @@ typedef enum pa_core_hook {
|
|||
PA_CORE_HOOK_SINK_INPUT_UNLINK_POST,
|
||||
PA_CORE_HOOK_SINK_INPUT_MOVE,
|
||||
PA_CORE_HOOK_SINK_INPUT_MOVE_POST,
|
||||
PA_CORE_HOOK_SINK_INPUT_NAME_CHANGED,
|
||||
PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED,
|
||||
PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED,
|
||||
PA_CORE_HOOK_SOURCE_OUTPUT_NEW,
|
||||
PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE,
|
||||
PA_CORE_HOOK_SOURCE_OUTPUT_PUT,
|
||||
|
|
@ -69,8 +74,8 @@ typedef enum pa_core_hook {
|
|||
PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST,
|
||||
PA_CORE_HOOK_SOURCE_OUTPUT_MOVE,
|
||||
PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_POST,
|
||||
PA_CORE_HOOK_SOURCE_OUTPUT_NAME_CHANGED,
|
||||
PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED,
|
||||
PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED,
|
||||
PA_CORE_HOOK_MAX
|
||||
} pa_core_hook_t;
|
||||
|
||||
|
|
@ -108,6 +113,7 @@ struct pa_core {
|
|||
pa_subscription_event *subscription_event_last;
|
||||
|
||||
pa_mempool *mempool;
|
||||
pa_silence_cache silence_cache;
|
||||
|
||||
int exit_idle_time, module_idle_time, scache_idle_time;
|
||||
|
||||
|
|
|
|||
|
|
@ -381,7 +381,7 @@ static void envelope_merge(pa_envelope *e, int v) {
|
|||
break;
|
||||
|
||||
if (e->points[v].n_points >= e->points[v].n_allocated) {
|
||||
e->points[v].n_allocated = MAX(e->points[v].n_points*2, PA_ENVELOPE_POINTS_MAX);
|
||||
e->points[v].n_allocated = PA_MAX(e->points[v].n_points*2, PA_ENVELOPE_POINTS_MAX);
|
||||
|
||||
e->points[v].x = pa_xrealloc(e->points[v].x, sizeof(size_t) * e->points[v].n_allocated);
|
||||
e->points[v].y.i = pa_xrealloc(e->points[v].y.i, sizeof(int32_t) * e->points[v].n_allocated);
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
#include <pulse/sample.h>
|
||||
|
||||
#define PA_ENVELOPE_POINTS_MAX 4
|
||||
#define PA_ENVELOPE_POINTS_MAX 4U
|
||||
|
||||
typedef struct pa_envelope pa_envelope;
|
||||
typedef struct pa_envelope_item pa_envelope_item;
|
||||
|
|
|
|||
|
|
@ -78,21 +78,19 @@ struct pa_fdsem {
|
|||
#ifdef HAVE_EVENTFD
|
||||
int efd;
|
||||
#endif
|
||||
pa_atomic_t waiting;
|
||||
pa_atomic_t signalled;
|
||||
pa_atomic_t in_pipe;
|
||||
|
||||
pa_fdsem_data *data;
|
||||
};
|
||||
|
||||
pa_fdsem *pa_fdsem_new(void) {
|
||||
pa_fdsem *f;
|
||||
|
||||
f = pa_xnew(pa_fdsem, 1);
|
||||
f = pa_xmalloc(PA_ALIGN(sizeof(pa_fdsem)) + PA_ALIGN(sizeof(pa_fdsem_data)));
|
||||
|
||||
#ifdef HAVE_EVENTFD
|
||||
if ((f->efd = eventfd(0)) >= 0) {
|
||||
pa_make_fd_cloexec(f->efd);
|
||||
f->fds[0] = f->fds[1] = -1;
|
||||
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
|
|
@ -105,9 +103,57 @@ pa_fdsem *pa_fdsem_new(void) {
|
|||
pa_make_fd_cloexec(f->fds[1]);
|
||||
}
|
||||
|
||||
pa_atomic_store(&f->waiting, 0);
|
||||
pa_atomic_store(&f->signalled, 0);
|
||||
pa_atomic_store(&f->in_pipe, 0);
|
||||
f->data = (pa_fdsem_data*) ((uint8_t*) f + PA_ALIGN(sizeof(pa_fdsem)));
|
||||
|
||||
pa_atomic_store(&f->data->waiting, 0);
|
||||
pa_atomic_store(&f->data->signalled, 0);
|
||||
pa_atomic_store(&f->data->in_pipe, 0);
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
pa_fdsem *pa_fdsem_open_shm(pa_fdsem_data *data, int event_fd) {
|
||||
pa_fdsem *f = NULL;
|
||||
|
||||
pa_assert(data);
|
||||
pa_assert(event_fd >= 0);
|
||||
|
||||
#ifdef HAVE_EVENTFD
|
||||
f = pa_xnew(pa_fdsem, 1);
|
||||
|
||||
f->efd = event_fd;
|
||||
pa_make_fd_cloexec(f->efd);
|
||||
f->fds[0] = f->fds[1] = -1;
|
||||
f->data = data;
|
||||
#endif
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
pa_fdsem *pa_fdsem_new_shm(pa_fdsem_data *data, int* event_fd) {
|
||||
pa_fdsem *f = NULL;
|
||||
|
||||
pa_assert(data);
|
||||
pa_assert(event_fd);
|
||||
|
||||
#ifdef HAVE_EVENTFD
|
||||
|
||||
f = pa_xnew(pa_fdsem, 1);
|
||||
|
||||
if ((f->efd = eventfd(0)) < 0) {
|
||||
pa_xfree(f);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pa_make_fd_cloexec(f->efd);
|
||||
f->fds[0] = f->fds[1] = -1;
|
||||
f->data = data;
|
||||
|
||||
pa_atomic_store(&f->data->waiting, 0);
|
||||
pa_atomic_store(&f->data->signalled, 0);
|
||||
pa_atomic_store(&f->data->in_pipe, 0);
|
||||
|
||||
#endif
|
||||
|
||||
return f;
|
||||
}
|
||||
|
|
@ -128,7 +174,7 @@ static void flush(pa_fdsem *f) {
|
|||
ssize_t r;
|
||||
pa_assert(f);
|
||||
|
||||
if (pa_atomic_load(&f->in_pipe) <= 0)
|
||||
if (pa_atomic_load(&f->data->in_pipe) <= 0)
|
||||
return;
|
||||
|
||||
do {
|
||||
|
|
@ -151,19 +197,19 @@ static void flush(pa_fdsem *f) {
|
|||
continue;
|
||||
}
|
||||
|
||||
} while (pa_atomic_sub(&f->in_pipe, r) > r);
|
||||
} while (pa_atomic_sub(&f->data->in_pipe, r) > r);
|
||||
}
|
||||
|
||||
void pa_fdsem_post(pa_fdsem *f) {
|
||||
pa_assert(f);
|
||||
|
||||
if (pa_atomic_cmpxchg(&f->signalled, 0, 1)) {
|
||||
if (pa_atomic_cmpxchg(&f->data->signalled, 0, 1)) {
|
||||
|
||||
if (pa_atomic_load(&f->waiting)) {
|
||||
if (pa_atomic_load(&f->data->waiting)) {
|
||||
ssize_t r;
|
||||
char x = 'x';
|
||||
|
||||
pa_atomic_inc(&f->in_pipe);
|
||||
pa_atomic_inc(&f->data->in_pipe);
|
||||
|
||||
for (;;) {
|
||||
|
||||
|
|
@ -194,12 +240,12 @@ void pa_fdsem_wait(pa_fdsem *f) {
|
|||
|
||||
flush(f);
|
||||
|
||||
if (pa_atomic_cmpxchg(&f->signalled, 1, 0))
|
||||
if (pa_atomic_cmpxchg(&f->data->signalled, 1, 0))
|
||||
return;
|
||||
|
||||
pa_atomic_inc(&f->waiting);
|
||||
pa_atomic_inc(&f->data->waiting);
|
||||
|
||||
while (!pa_atomic_cmpxchg(&f->signalled, 1, 0)) {
|
||||
while (!pa_atomic_cmpxchg(&f->data->signalled, 1, 0)) {
|
||||
char x[10];
|
||||
ssize_t r;
|
||||
|
||||
|
|
@ -221,10 +267,10 @@ void pa_fdsem_wait(pa_fdsem *f) {
|
|||
continue;
|
||||
}
|
||||
|
||||
pa_atomic_sub(&f->in_pipe, r);
|
||||
pa_atomic_sub(&f->data->in_pipe, r);
|
||||
}
|
||||
|
||||
pa_assert_se(pa_atomic_dec(&f->waiting) >= 1);
|
||||
pa_assert_se(pa_atomic_dec(&f->data->waiting) >= 1);
|
||||
}
|
||||
|
||||
int pa_fdsem_try(pa_fdsem *f) {
|
||||
|
|
@ -232,7 +278,7 @@ int pa_fdsem_try(pa_fdsem *f) {
|
|||
|
||||
flush(f);
|
||||
|
||||
if (pa_atomic_cmpxchg(&f->signalled, 1, 0))
|
||||
if (pa_atomic_cmpxchg(&f->data->signalled, 1, 0))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
|
|
@ -254,13 +300,13 @@ int pa_fdsem_before_poll(pa_fdsem *f) {
|
|||
|
||||
flush(f);
|
||||
|
||||
if (pa_atomic_cmpxchg(&f->signalled, 1, 0))
|
||||
if (pa_atomic_cmpxchg(&f->data->signalled, 1, 0))
|
||||
return -1;
|
||||
|
||||
pa_atomic_inc(&f->waiting);
|
||||
pa_atomic_inc(&f->data->waiting);
|
||||
|
||||
if (pa_atomic_cmpxchg(&f->signalled, 1, 0)) {
|
||||
pa_assert_se(pa_atomic_dec(&f->waiting) >= 1);
|
||||
if (pa_atomic_cmpxchg(&f->data->signalled, 1, 0)) {
|
||||
pa_assert_se(pa_atomic_dec(&f->data->waiting) >= 1);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
|
|
@ -269,11 +315,11 @@ int pa_fdsem_before_poll(pa_fdsem *f) {
|
|||
int pa_fdsem_after_poll(pa_fdsem *f) {
|
||||
pa_assert(f);
|
||||
|
||||
pa_assert_se(pa_atomic_dec(&f->waiting) >= 1);
|
||||
pa_assert_se(pa_atomic_dec(&f->data->waiting) >= 1);
|
||||
|
||||
flush(f);
|
||||
|
||||
if (pa_atomic_cmpxchg(&f->signalled, 1, 0))
|
||||
if (pa_atomic_cmpxchg(&f->data->signalled, 1, 0))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -33,7 +33,15 @@
|
|||
|
||||
typedef struct pa_fdsem pa_fdsem;
|
||||
|
||||
typedef struct pa_fdsem_data {
|
||||
pa_atomic_t waiting;
|
||||
pa_atomic_t signalled;
|
||||
pa_atomic_t in_pipe;
|
||||
} pa_fdsem_data;
|
||||
|
||||
pa_fdsem *pa_fdsem_new(void);
|
||||
pa_fdsem *pa_fdsem_open_shm(pa_fdsem_data *data, int event_fd);
|
||||
pa_fdsem *pa_fdsem_new_shm(pa_fdsem_data *data, int* event_fd);
|
||||
void pa_fdsem_free(pa_fdsem *f);
|
||||
|
||||
void pa_fdsem_post(pa_fdsem *f);
|
||||
|
|
|
|||
|
|
@ -25,9 +25,9 @@
|
|||
***/
|
||||
|
||||
#include <pulse/def.h>
|
||||
#include <pulse/gccmacro.h>
|
||||
|
||||
#include <pulsecore/once.h>
|
||||
#include <pulsecore/gccmacro.h>
|
||||
|
||||
/* A multiple-reader multipler-write lock-free free list implementation */
|
||||
|
||||
|
|
|
|||
|
|
@ -1,90 +0,0 @@
|
|||
#ifndef foopulsegccmacrohfoo
|
||||
#define foopulsegccmacrohfoo
|
||||
|
||||
/* $Id$ */
|
||||
|
||||
/***
|
||||
This file is part of PulseAudio.
|
||||
|
||||
Copyright 2004-2006 Lennart Poettering
|
||||
|
||||
PulseAudio is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published
|
||||
by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
PulseAudio is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with PulseAudio; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
USA.
|
||||
***/
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define PA_GCC_PRINTF_ATTR(a,b) __attribute__ ((format (printf, a, b)))
|
||||
#else
|
||||
/** If we're in GNU C, use some magic for detecting invalid format strings */
|
||||
#define PA_GCC_PRINTF_ATTR(a,b)
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) && (__GNUC__ >= 4)
|
||||
#define PA_GCC_SENTINEL __attribute__ ((sentinel))
|
||||
#else
|
||||
/** Macro for usage of GCC's sentinel compilation warnings */
|
||||
#define PA_GCC_SENTINEL
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define PA_GCC_NORETURN __attribute__((noreturn))
|
||||
#else
|
||||
/** Macro for no-return functions */
|
||||
#define PA_GCC_NORETURN
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define PA_GCC_UNUSED __attribute__ ((unused))
|
||||
#else
|
||||
/** Macro for not used parameter */
|
||||
#define PA_GCC_UNUSED
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define PA_GCC_DESTRUCTOR __attribute__ ((destructor))
|
||||
#else
|
||||
/** Call this function when process terminates */
|
||||
#define PA_GCC_DESTRUCTOR
|
||||
#endif
|
||||
|
||||
#ifndef PA_GCC_PURE
|
||||
#ifdef __GNUC__
|
||||
#define PA_GCC_PURE __attribute__ ((pure))
|
||||
#else
|
||||
/** This function's return value depends only the arguments list and global state **/
|
||||
#define PA_GCC_PURE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef PA_GCC_CONST
|
||||
#ifdef __GNUC__
|
||||
#define PA_GCC_CONST __attribute__ ((const))
|
||||
#else
|
||||
/** This function's return value depends only the arguments list (stricter version of PA_GCC_PURE) **/
|
||||
#define PA_GCC_CONST
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef PA_LIKELY
|
||||
#ifdef __GNUC__
|
||||
#define PA_LIKELY(x) (__builtin_expect(!!(x),1))
|
||||
#define PA_UNLIKELY(x) (__builtin_expect((x),0))
|
||||
#else
|
||||
#define PA_LIKELY(x) (x)
|
||||
#define PA_UNLIKELY(x) (x)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -24,9 +24,10 @@
|
|||
USA.
|
||||
***/
|
||||
|
||||
#include <pulsecore/llist.h>
|
||||
#include <pulse/xmalloc.h>
|
||||
#include <pulsecore/gccmacro.h>
|
||||
#include <pulse/gccmacro.h>
|
||||
|
||||
#include <pulsecore/llist.h>
|
||||
|
||||
typedef struct pa_hook_slot pa_hook_slot;
|
||||
typedef struct pa_hook pa_hook;
|
||||
|
|
|
|||
|
|
@ -49,7 +49,6 @@ struct pa_ioline {
|
|||
pa_iochannel *io;
|
||||
pa_defer_event *defer_event;
|
||||
pa_mainloop_api *mainloop;
|
||||
int dead;
|
||||
|
||||
char *wbuf;
|
||||
size_t wbuf_length, wbuf_index, wbuf_valid_length;
|
||||
|
|
@ -57,10 +56,11 @@ struct pa_ioline {
|
|||
char *rbuf;
|
||||
size_t rbuf_length, rbuf_index, rbuf_valid_length;
|
||||
|
||||
void (*callback)(pa_ioline*io, const char *s, void *userdata);
|
||||
pa_ioline_cb_t callback;
|
||||
void *userdata;
|
||||
|
||||
int defer_close;
|
||||
pa_bool_t dead:1;
|
||||
pa_bool_t defer_close:1;
|
||||
};
|
||||
|
||||
static void io_callback(pa_iochannel*io, void *userdata);
|
||||
|
|
@ -73,7 +73,6 @@ pa_ioline* pa_ioline_new(pa_iochannel *io) {
|
|||
l = pa_xnew(pa_ioline, 1);
|
||||
PA_REFCNT_INIT(l);
|
||||
l->io = io;
|
||||
l->dead = 0;
|
||||
|
||||
l->wbuf = NULL;
|
||||
l->wbuf_length = l->wbuf_index = l->wbuf_valid_length = 0;
|
||||
|
|
@ -89,7 +88,8 @@ pa_ioline* pa_ioline_new(pa_iochannel *io) {
|
|||
l->defer_event = l->mainloop->defer_new(l->mainloop, defer_callback, l);
|
||||
l->mainloop->defer_enable(l->defer_event, 0);
|
||||
|
||||
l->defer_close = 0;
|
||||
l->dead = FALSE;
|
||||
l->defer_close = FALSE;
|
||||
|
||||
pa_iochannel_set_callback(io, io_callback, l);
|
||||
|
||||
|
|
@ -130,7 +130,7 @@ void pa_ioline_close(pa_ioline *l) {
|
|||
pa_assert(l);
|
||||
pa_assert(PA_REFCNT_VALUE(l) >= 1);
|
||||
|
||||
l->dead = 1;
|
||||
l->dead = TRUE;
|
||||
|
||||
if (l->io) {
|
||||
pa_iochannel_free(l->io);
|
||||
|
|
@ -166,11 +166,13 @@ void pa_ioline_puts(pa_ioline *l, const char *c) {
|
|||
/* In case the allocated buffer is too small, enlarge it. */
|
||||
if (l->wbuf_valid_length + len > l->wbuf_length) {
|
||||
size_t n = l->wbuf_valid_length+len;
|
||||
char *new = pa_xmalloc(n);
|
||||
char *new = pa_xnew(char, n);
|
||||
|
||||
if (l->wbuf) {
|
||||
memcpy(new, l->wbuf+l->wbuf_index, l->wbuf_valid_length);
|
||||
pa_xfree(l->wbuf);
|
||||
}
|
||||
|
||||
l->wbuf = new;
|
||||
l->wbuf_length = n;
|
||||
l->wbuf_index = 0;
|
||||
|
|
@ -191,15 +193,18 @@ void pa_ioline_puts(pa_ioline *l, const char *c) {
|
|||
}
|
||||
}
|
||||
|
||||
void pa_ioline_set_callback(pa_ioline*l, void (*callback)(pa_ioline*io, const char *s, void *userdata), void *userdata) {
|
||||
void pa_ioline_set_callback(pa_ioline*l, pa_ioline_cb_t callback, void *userdata) {
|
||||
pa_assert(l);
|
||||
pa_assert(PA_REFCNT_VALUE(l) >= 1);
|
||||
|
||||
if (l->dead)
|
||||
return;
|
||||
|
||||
l->callback = callback;
|
||||
l->userdata = userdata;
|
||||
}
|
||||
|
||||
static void failure(pa_ioline *l, int process_leftover) {
|
||||
static void failure(pa_ioline *l, pa_bool_t process_leftover) {
|
||||
pa_assert(l);
|
||||
pa_assert(PA_REFCNT_VALUE(l) >= 1);
|
||||
pa_assert(!l->dead);
|
||||
|
|
@ -247,7 +252,7 @@ static void scan_for_lines(pa_ioline *l, size_t skip) {
|
|||
l->rbuf_index = 0;
|
||||
|
||||
if (l->callback)
|
||||
l->callback(l, p, l->userdata);
|
||||
l->callback(l, pa_strip_nl(p), l->userdata);
|
||||
|
||||
skip = 0;
|
||||
}
|
||||
|
|
@ -282,7 +287,7 @@ static int do_read(pa_ioline *l) {
|
|||
memmove(l->rbuf, l->rbuf+l->rbuf_index, l->rbuf_valid_length);
|
||||
} else {
|
||||
/* Enlarge the buffer */
|
||||
char *new = pa_xmalloc(n);
|
||||
char *new = pa_xnew(char, n);
|
||||
if (l->rbuf_valid_length)
|
||||
memcpy(new, l->rbuf+l->rbuf_index, l->rbuf_valid_length);
|
||||
pa_xfree(l->rbuf);
|
||||
|
|
@ -299,11 +304,15 @@ static int do_read(pa_ioline *l) {
|
|||
|
||||
/* Read some data */
|
||||
if ((r = pa_iochannel_read(l->io, l->rbuf+l->rbuf_index+l->rbuf_valid_length, len)) <= 0) {
|
||||
|
||||
if (r < 0 && errno == EAGAIN)
|
||||
return 0;
|
||||
|
||||
if (r < 0 && errno != ECONNRESET) {
|
||||
pa_log("read(): %s", pa_cstrerror(errno));
|
||||
failure(l, 0);
|
||||
failure(l, FALSE);
|
||||
} else
|
||||
failure(l, 1);
|
||||
failure(l, TRUE);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -328,10 +337,13 @@ static int do_write(pa_ioline *l) {
|
|||
|
||||
if ((r = pa_iochannel_write(l->io, l->wbuf+l->wbuf_index, l->wbuf_valid_length)) <= 0) {
|
||||
|
||||
if (r < 0 && errno == EAGAIN)
|
||||
return 0;
|
||||
|
||||
if (r < 0 && errno != EPIPE)
|
||||
pa_log("write(): %s", pa_cstrerror(errno));
|
||||
|
||||
failure(l, 0);
|
||||
failure(l, FALSE);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -363,7 +375,7 @@ static void do_work(pa_ioline *l) {
|
|||
do_write(l);
|
||||
|
||||
if (l->defer_close && !l->wbuf_valid_length)
|
||||
failure(l, 1);
|
||||
failure(l, TRUE);
|
||||
|
||||
pa_ioline_unref(l);
|
||||
}
|
||||
|
|
@ -393,7 +405,7 @@ void pa_ioline_defer_close(pa_ioline *l) {
|
|||
pa_assert(l);
|
||||
pa_assert(PA_REFCNT_VALUE(l) >= 1);
|
||||
|
||||
l->defer_close = 1;
|
||||
l->defer_close = TRUE;
|
||||
|
||||
if (!l->wbuf_valid_length)
|
||||
l->mainloop->defer_enable(l->defer_event, 1);
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@
|
|||
|
||||
typedef struct pa_ioline pa_ioline;
|
||||
|
||||
typedef void (*pa_ioline_cb_t)(pa_ioline*io, const char *s, void *userdata);
|
||||
|
||||
pa_ioline* pa_ioline_new(pa_iochannel *io);
|
||||
void pa_ioline_unref(pa_ioline *l);
|
||||
pa_ioline* pa_ioline_ref(pa_ioline *l);
|
||||
|
|
@ -45,7 +47,7 @@ void pa_ioline_puts(pa_ioline *s, const char *c);
|
|||
void pa_ioline_printf(pa_ioline *s, const char *format, ...) PA_GCC_PRINTF_ATTR(2,3);
|
||||
|
||||
/* Set the callback function that is called for every recieved line */
|
||||
void pa_ioline_set_callback(pa_ioline*io, void (*callback)(pa_ioline*io, const char *s, void *userdata), void *userdata);
|
||||
void pa_ioline_set_callback(pa_ioline*io, pa_ioline_cb_t callback, void *userdata);
|
||||
|
||||
/* Make sure to close the ioline object as soon as the send buffer is emptied */
|
||||
void pa_ioline_defer_close(pa_ioline *io);
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef HAVE_SYSLOG_H
|
||||
#include <syslog.h>
|
||||
|
|
@ -108,7 +109,12 @@ void pa_log_levelv_meta(
|
|||
va_list ap) {
|
||||
|
||||
const char *e;
|
||||
char *text, *t, *n, *location;
|
||||
char *t, *n;
|
||||
int saved_errno = errno;
|
||||
|
||||
/* We don't use dynamic memory allocation here to minimize the hit
|
||||
* in RT threads */
|
||||
char text[1024], location[128];
|
||||
|
||||
pa_assert(level < PA_LOG_LEVEL_MAX);
|
||||
pa_assert(format);
|
||||
|
|
@ -116,17 +122,19 @@ void pa_log_levelv_meta(
|
|||
if ((e = getenv(ENV_LOGLEVEL)))
|
||||
maximal_level = atoi(e);
|
||||
|
||||
if (level > maximal_level)
|
||||
if (level > maximal_level) {
|
||||
errno = saved_errno;
|
||||
return;
|
||||
}
|
||||
|
||||
text = pa_vsprintf_malloc(format, ap);
|
||||
pa_vsnprintf(text, sizeof(text), format, ap);
|
||||
|
||||
if (getenv(ENV_LOGMETA) && file && line > 0 && func)
|
||||
location = pa_sprintf_malloc("[%s:%i %s()] ", file, line, func);
|
||||
pa_snprintf(location, sizeof(location), "[%s:%i %s()] ", file, line, func);
|
||||
else if (file)
|
||||
location = pa_sprintf_malloc("%s: ", pa_path_get_filename(file));
|
||||
pa_snprintf(location, sizeof(location), "%s: ", pa_path_get_filename(file));
|
||||
else
|
||||
location = pa_xstrdup("");
|
||||
location[0] = 0;
|
||||
|
||||
if (!pa_utf8_valid(text))
|
||||
pa_log_level(level, __FILE__": invalid UTF-8 string following below:");
|
||||
|
|
@ -158,6 +166,8 @@ void pa_log_levelv_meta(
|
|||
}
|
||||
#endif
|
||||
|
||||
/* We shouldn't be using dynamic allocation here to
|
||||
* minimize the hit in RT threads */
|
||||
local_t = pa_utf8_to_locale(t);
|
||||
if (!local_t)
|
||||
fprintf(stderr, "%c: %s%s%s%s\n", level_to_char[level], location, prefix, t, suffix);
|
||||
|
|
@ -189,11 +199,10 @@ void pa_log_levelv_meta(
|
|||
#endif
|
||||
|
||||
case PA_LOG_USER: {
|
||||
char *x;
|
||||
char x[1024];
|
||||
|
||||
x = pa_sprintf_malloc("%s%s", location, t);
|
||||
pa_snprintf(x, sizeof(x), "%s%s", location, t);
|
||||
user_log_func(level, x);
|
||||
pa_xfree(x);
|
||||
|
||||
break;
|
||||
}
|
||||
|
|
@ -204,8 +213,7 @@ void pa_log_levelv_meta(
|
|||
}
|
||||
}
|
||||
|
||||
pa_xfree(text);
|
||||
pa_xfree(location);
|
||||
errno = saved_errno;
|
||||
}
|
||||
|
||||
void pa_log_level_meta(
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <pulsecore/gccmacro.h>
|
||||
#include <pulse/gccmacro.h>
|
||||
|
||||
/* A simple logging subsystem */
|
||||
|
||||
|
|
|
|||
|
|
@ -42,12 +42,14 @@ pa_void_func_t pa_load_sym(lt_dlhandle handle, const char *module, const char *s
|
|||
pa_void_func_t f;
|
||||
|
||||
pa_assert(handle);
|
||||
pa_assert(module);
|
||||
pa_assert(symbol);
|
||||
|
||||
if ((f = ((pa_void_func_t) (long) lt_dlsym(handle, symbol))))
|
||||
if ((f = ((pa_void_func_t) (size_t) lt_dlsym(handle, symbol))))
|
||||
return f;
|
||||
|
||||
if (!module)
|
||||
return NULL;
|
||||
|
||||
/* As the .la files might have been cleansed from the system, we should
|
||||
* try with the ltdl prefix as well. */
|
||||
|
||||
|
|
@ -57,7 +59,7 @@ pa_void_func_t pa_load_sym(lt_dlhandle handle, const char *module, const char *s
|
|||
if (!isalnum(*c))
|
||||
*c = '_';
|
||||
|
||||
f = (pa_void_func_t) (long) lt_dlsym(handle, sn);
|
||||
f = (pa_void_func_t) (size_t) lt_dlsym(handle, sn);
|
||||
pa_xfree(sn);
|
||||
|
||||
return f;
|
||||
|
|
|
|||
|
|
@ -33,12 +33,22 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include <pulsecore/log.h>
|
||||
#include <pulsecore/gccmacro.h>
|
||||
#include <pulse/gccmacro.h>
|
||||
|
||||
#ifndef PACKAGE
|
||||
#error "Please include config.h before including this file!"
|
||||
#endif
|
||||
|
||||
#ifndef PA_LIKELY
|
||||
#ifdef __GNUC__
|
||||
#define PA_LIKELY(x) (__builtin_expect(!!(x),1))
|
||||
#define PA_UNLIKELY(x) (__builtin_expect((x),0))
|
||||
#else
|
||||
#define PA_LIKELY(x) (x)
|
||||
#define PA_UNLIKELY(x) (x)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(PAGE_SIZE)
|
||||
#define PA_PAGE_SIZE ((size_t) PAGE_SIZE)
|
||||
#elif defined(PAGESIZE)
|
||||
|
|
@ -67,19 +77,53 @@ static inline size_t pa_page_align(size_t l) {
|
|||
|
||||
#define PA_ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0]))
|
||||
|
||||
#ifndef MAX
|
||||
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
/* The users of PA_MIN and PA_MAX should be aware that these macros on
|
||||
* non-GCC executed code with side effects twice. It is thus
|
||||
* considered misuse to use code with side effects as arguments to MIN
|
||||
* and MAX. */
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define PA_MAX(a,b) \
|
||||
__extension__ ({ typeof(a) _a = (a); \
|
||||
typeof(b) _b = (b); \
|
||||
_a > _b ? _a : _b; \
|
||||
})
|
||||
#else
|
||||
#define PA_MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
#ifdef __GNUC__
|
||||
#define PA_MIN(a,b) \
|
||||
__extension__ ({ typeof(a) _a = (a); \
|
||||
typeof(b) _b = (b); \
|
||||
_a < _b ? _a : _b; \
|
||||
})
|
||||
#else
|
||||
#define PA_MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#ifndef CLAMP
|
||||
#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
|
||||
#ifdef __GNUC__
|
||||
#define PA_CLAMP(x, low, high) \
|
||||
__extension__ ({ typeof(x) _x = (x); \
|
||||
typeof(low) _low = (low); \
|
||||
typeof(high) _high = (high); \
|
||||
((_x > _high) ? _high : ((_x < _low) ? _low : _x)); \
|
||||
})
|
||||
#else
|
||||
#define PA_CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define PA_CLAMP_UNLIKELY(x, low, high) \
|
||||
__extension__ ({ typeof(x) _x = (x); \
|
||||
typeof(low) _low = (low); \
|
||||
typeof(high) _high = (high); \
|
||||
(PA_UNLIKELY(_x > _high) ? _high : (PA_UNLIKELY(_x < _low) ? _low : _x)); \
|
||||
})
|
||||
#else
|
||||
#define PA_CLAMP_UNLIKELY(x, low, high) (PA_UNLIKELY((x) > (high)) ? (high) : (PA_UNLIKELY((x) < (low)) ? (low) : (x)))
|
||||
#endif
|
||||
|
||||
/* We don't define a PA_CLAMP_LIKELY here, because it doesn't really
|
||||
* make sense: we cannot know if it is more likely that the values is
|
||||
* lower or greater than the boundaries.*/
|
||||
|
|
@ -166,8 +210,17 @@ typedef int pa_bool_t;
|
|||
#define PA_PATH_SEP_CHAR '/'
|
||||
#endif
|
||||
|
||||
static inline const char *pa_strnull(const char *x) {
|
||||
return x ? x : "(null)";
|
||||
}
|
||||
#ifdef __GNUC__
|
||||
|
||||
#define PA_WARN_REFERENCE(sym, msg) \
|
||||
__asm__(".section .gnu.warning." #sym); \
|
||||
__asm__(".asciz \"" msg "\""); \
|
||||
__asm__(".previous")
|
||||
|
||||
#else
|
||||
|
||||
#define PA_WARN_REFERENCE(sym, msg)
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -197,7 +197,6 @@ int pa_mcalign_pop(pa_mcalign *m, pa_memchunk *c) {
|
|||
|
||||
/* There's simply nothing */
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
size_t pa_mcalign_csize(pa_mcalign *m, size_t l) {
|
||||
|
|
@ -211,3 +210,11 @@ size_t pa_mcalign_csize(pa_mcalign *m, size_t l) {
|
|||
|
||||
return (l/m->base)*m->base;
|
||||
}
|
||||
|
||||
void pa_mcalign_flush(pa_mcalign *m) {
|
||||
pa_memchunk chunk;
|
||||
pa_assert(m);
|
||||
|
||||
while (pa_mcalign_pop(m, &chunk) >= 0)
|
||||
pa_memblock_unref(chunk.memblock);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,4 +79,7 @@ int pa_mcalign_pop(pa_mcalign *m, pa_memchunk *c);
|
|||
/* If we pass l bytes in now, how many bytes would we get out? */
|
||||
size_t pa_mcalign_csize(pa_mcalign *m, size_t l);
|
||||
|
||||
/* Flush what's still stored in the aligner */
|
||||
void pa_mcalign_flush(pa_mcalign *m);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -46,8 +46,12 @@
|
|||
|
||||
#include "memblock.h"
|
||||
|
||||
#define PA_MEMPOOL_SLOTS_MAX 128
|
||||
#define PA_MEMPOOL_SLOT_SIZE (16*1024)
|
||||
/* We can allocate 64*1024*1024 bytes at maximum. That's 64MB. Please
|
||||
* note that the footprint is usually much smaller, since the data is
|
||||
* stored in SHM and our OS does not commit the memory before we use
|
||||
* it for the first time. */
|
||||
#define PA_MEMPOOL_SLOTS_MAX 1024
|
||||
#define PA_MEMPOOL_SLOT_SIZE (64*1024)
|
||||
|
||||
#define PA_MEMEXPORT_SLOTS_MAX 128
|
||||
|
||||
|
|
@ -59,7 +63,9 @@ struct pa_memblock {
|
|||
pa_mempool *pool;
|
||||
|
||||
pa_memblock_type_t type;
|
||||
int read_only; /* boolean */
|
||||
|
||||
pa_bool_t read_only:1;
|
||||
pa_bool_t is_silence:1;
|
||||
|
||||
pa_atomic_ptr_t data;
|
||||
size_t length;
|
||||
|
|
@ -125,11 +131,6 @@ struct pa_memexport {
|
|||
PA_LLIST_FIELDS(pa_memexport);
|
||||
};
|
||||
|
||||
struct mempool_slot {
|
||||
PA_LLIST_FIELDS(struct mempool_slot);
|
||||
/* the actual data follows immediately hereafter */
|
||||
};
|
||||
|
||||
struct pa_mempool {
|
||||
pa_semaphore *semaphore;
|
||||
pa_mutex *mutex;
|
||||
|
|
@ -202,7 +203,7 @@ pa_memblock *pa_memblock_new(pa_mempool *p, size_t length) {
|
|||
pa_memblock *b;
|
||||
|
||||
pa_assert(p);
|
||||
pa_assert(length > 0);
|
||||
pa_assert(length);
|
||||
|
||||
if (!(b = pa_memblock_new_pool(p, length)))
|
||||
b = memblock_new_appended(p, length);
|
||||
|
|
@ -215,18 +216,18 @@ static pa_memblock *memblock_new_appended(pa_mempool *p, size_t length) {
|
|||
pa_memblock *b;
|
||||
|
||||
pa_assert(p);
|
||||
pa_assert(length > 0);
|
||||
pa_assert(length);
|
||||
|
||||
/* If -1 is passed as length we choose the size for the caller. */
|
||||
|
||||
if (length == (size_t) -1)
|
||||
length = p->block_size - PA_ALIGN(sizeof(struct mempool_slot)) - PA_ALIGN(sizeof(pa_memblock));
|
||||
length = p->block_size - PA_ALIGN(sizeof(pa_memblock));
|
||||
|
||||
b = pa_xmalloc(PA_ALIGN(sizeof(pa_memblock)) + length);
|
||||
PA_REFCNT_INIT(b);
|
||||
b->pool = p;
|
||||
b->type = PA_MEMBLOCK_APPENDED;
|
||||
b->read_only = 0;
|
||||
b->read_only = b->is_silence = FALSE;
|
||||
pa_atomic_ptr_store(&b->data, (uint8_t*) b + PA_ALIGN(sizeof(pa_memblock)));
|
||||
b->length = length;
|
||||
pa_atomic_store(&b->n_acquired, 0);
|
||||
|
|
@ -252,7 +253,7 @@ static struct mempool_slot* mempool_allocate_slot(pa_mempool *p) {
|
|||
slot = (struct mempool_slot*) ((uint8_t*) p->memory.ptr + (p->block_size * idx));
|
||||
|
||||
if (!slot) {
|
||||
pa_log_debug("Pool full");
|
||||
pa_log_info("Pool full");
|
||||
pa_atomic_inc(&p->stat.n_pool_full);
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -261,11 +262,9 @@ static struct mempool_slot* mempool_allocate_slot(pa_mempool *p) {
|
|||
return slot;
|
||||
}
|
||||
|
||||
/* No lock necessary */
|
||||
static void* mempool_slot_data(struct mempool_slot *slot) {
|
||||
pa_assert(slot);
|
||||
|
||||
return (uint8_t*) slot + PA_ALIGN(sizeof(struct mempool_slot));
|
||||
/* No lock necessary, totally redundant anyway */
|
||||
static inline void* mempool_slot_data(struct mempool_slot *slot) {
|
||||
return slot;
|
||||
}
|
||||
|
||||
/* No lock necessary */
|
||||
|
|
@ -294,7 +293,7 @@ pa_memblock *pa_memblock_new_pool(pa_mempool *p, size_t length) {
|
|||
struct mempool_slot *slot;
|
||||
|
||||
pa_assert(p);
|
||||
pa_assert(length > 0);
|
||||
pa_assert(length);
|
||||
|
||||
/* If -1 is passed as length we choose the size for the caller: we
|
||||
* take the largest size that fits in one of our slots. */
|
||||
|
|
@ -302,7 +301,7 @@ pa_memblock *pa_memblock_new_pool(pa_mempool *p, size_t length) {
|
|||
if (length == (size_t) -1)
|
||||
length = pa_mempool_block_size_max(p);
|
||||
|
||||
if (p->block_size - PA_ALIGN(sizeof(struct mempool_slot)) >= PA_ALIGN(sizeof(pa_memblock)) + length) {
|
||||
if (p->block_size >= PA_ALIGN(sizeof(pa_memblock)) + length) {
|
||||
|
||||
if (!(slot = mempool_allocate_slot(p)))
|
||||
return NULL;
|
||||
|
|
@ -311,7 +310,7 @@ pa_memblock *pa_memblock_new_pool(pa_mempool *p, size_t length) {
|
|||
b->type = PA_MEMBLOCK_POOL;
|
||||
pa_atomic_ptr_store(&b->data, (uint8_t*) b + PA_ALIGN(sizeof(pa_memblock)));
|
||||
|
||||
} else if (p->block_size - PA_ALIGN(sizeof(struct mempool_slot)) >= length) {
|
||||
} else if (p->block_size >= length) {
|
||||
|
||||
if (!(slot = mempool_allocate_slot(p)))
|
||||
return NULL;
|
||||
|
|
@ -323,14 +322,14 @@ pa_memblock *pa_memblock_new_pool(pa_mempool *p, size_t length) {
|
|||
pa_atomic_ptr_store(&b->data, mempool_slot_data(slot));
|
||||
|
||||
} else {
|
||||
pa_log_debug("Memory block too large for pool: %lu > %lu", (unsigned long) length, (unsigned long) (p->block_size - PA_ALIGN(sizeof(struct mempool_slot))));
|
||||
pa_log_debug("Memory block too large for pool: %lu > %lu", (unsigned long) length, (unsigned long) p->block_size);
|
||||
pa_atomic_inc(&p->stat.n_too_large_for_pool);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PA_REFCNT_INIT(b);
|
||||
b->pool = p;
|
||||
b->read_only = 0;
|
||||
b->read_only = b->is_silence = FALSE;
|
||||
b->length = length;
|
||||
pa_atomic_store(&b->n_acquired, 0);
|
||||
pa_atomic_store(&b->please_signal, 0);
|
||||
|
|
@ -340,13 +339,13 @@ pa_memblock *pa_memblock_new_pool(pa_mempool *p, size_t length) {
|
|||
}
|
||||
|
||||
/* No lock necessary */
|
||||
pa_memblock *pa_memblock_new_fixed(pa_mempool *p, void *d, size_t length, int read_only) {
|
||||
pa_memblock *pa_memblock_new_fixed(pa_mempool *p, void *d, size_t length, pa_bool_t read_only) {
|
||||
pa_memblock *b;
|
||||
|
||||
pa_assert(p);
|
||||
pa_assert(d);
|
||||
pa_assert(length != (size_t) -1);
|
||||
pa_assert(length > 0);
|
||||
pa_assert(length);
|
||||
|
||||
if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks))))
|
||||
b = pa_xnew(pa_memblock, 1);
|
||||
|
|
@ -354,6 +353,7 @@ pa_memblock *pa_memblock_new_fixed(pa_mempool *p, void *d, size_t length, int re
|
|||
b->pool = p;
|
||||
b->type = PA_MEMBLOCK_FIXED;
|
||||
b->read_only = read_only;
|
||||
b->is_silence = FALSE;
|
||||
pa_atomic_ptr_store(&b->data, d);
|
||||
b->length = length;
|
||||
pa_atomic_store(&b->n_acquired, 0);
|
||||
|
|
@ -364,12 +364,12 @@ pa_memblock *pa_memblock_new_fixed(pa_mempool *p, void *d, size_t length, int re
|
|||
}
|
||||
|
||||
/* No lock necessary */
|
||||
pa_memblock *pa_memblock_new_user(pa_mempool *p, void *d, size_t length, void (*free_cb)(void *p), int read_only) {
|
||||
pa_memblock *pa_memblock_new_user(pa_mempool *p, void *d, size_t length, pa_free_cb_t free_cb, pa_bool_t read_only) {
|
||||
pa_memblock *b;
|
||||
|
||||
pa_assert(p);
|
||||
pa_assert(d);
|
||||
pa_assert(length > 0);
|
||||
pa_assert(length);
|
||||
pa_assert(length != (size_t) -1);
|
||||
pa_assert(free_cb);
|
||||
|
||||
|
|
@ -379,6 +379,7 @@ pa_memblock *pa_memblock_new_user(pa_mempool *p, void *d, size_t length, void (*
|
|||
b->pool = p;
|
||||
b->type = PA_MEMBLOCK_USER;
|
||||
b->read_only = read_only;
|
||||
b->is_silence = FALSE;
|
||||
pa_atomic_ptr_store(&b->data, d);
|
||||
b->length = length;
|
||||
pa_atomic_store(&b->n_acquired, 0);
|
||||
|
|
@ -391,7 +392,7 @@ pa_memblock *pa_memblock_new_user(pa_mempool *p, void *d, size_t length, void (*
|
|||
}
|
||||
|
||||
/* No lock necessary */
|
||||
int pa_memblock_is_read_only(pa_memblock *b) {
|
||||
pa_bool_t pa_memblock_is_read_only(pa_memblock *b) {
|
||||
pa_assert(b);
|
||||
pa_assert(PA_REFCNT_VALUE(b) > 0);
|
||||
|
||||
|
|
@ -399,13 +400,27 @@ int pa_memblock_is_read_only(pa_memblock *b) {
|
|||
}
|
||||
|
||||
/* No lock necessary */
|
||||
int pa_memblock_ref_is_one(pa_memblock *b) {
|
||||
int r;
|
||||
pa_bool_t pa_memblock_is_silence(pa_memblock *b) {
|
||||
pa_assert(b);
|
||||
pa_assert(PA_REFCNT_VALUE(b) > 0);
|
||||
|
||||
return b->is_silence;
|
||||
}
|
||||
|
||||
/* No lock necessary */
|
||||
void pa_memblock_set_is_silence(pa_memblock *b, pa_bool_t v) {
|
||||
pa_assert(b);
|
||||
pa_assert(PA_REFCNT_VALUE(b) > 0);
|
||||
|
||||
b->is_silence = v;
|
||||
}
|
||||
|
||||
/* No lock necessary */
|
||||
pa_bool_t pa_memblock_ref_is_one(pa_memblock *b) {
|
||||
int r;
|
||||
pa_assert(b);
|
||||
|
||||
r = PA_REFCNT_VALUE(b);
|
||||
pa_assert(r > 0);
|
||||
pa_assert_se((r = PA_REFCNT_VALUE(b)) > 0);
|
||||
|
||||
return r == 1;
|
||||
}
|
||||
|
|
@ -567,7 +582,7 @@ static void memblock_make_local(pa_memblock *b) {
|
|||
|
||||
pa_atomic_dec(&b->pool->stat.n_allocated_by_type[b->type]);
|
||||
|
||||
if (b->length <= b->pool->block_size - PA_ALIGN(sizeof(struct mempool_slot))) {
|
||||
if (b->length <= b->pool->block_size) {
|
||||
struct mempool_slot *slot;
|
||||
|
||||
if ((slot = mempool_allocate_slot(b->pool))) {
|
||||
|
|
@ -579,7 +594,7 @@ static void memblock_make_local(pa_memblock *b) {
|
|||
pa_atomic_ptr_store(&b->data, new_data);
|
||||
|
||||
b->type = PA_MEMBLOCK_POOL_EXTERNAL;
|
||||
b->read_only = 0;
|
||||
b->read_only = FALSE;
|
||||
|
||||
goto finish;
|
||||
}
|
||||
|
|
@ -590,7 +605,7 @@ static void memblock_make_local(pa_memblock *b) {
|
|||
pa_atomic_ptr_store(&b->data, pa_xmemdup(pa_atomic_ptr_load(&b->data), b->length));
|
||||
|
||||
b->type = PA_MEMBLOCK_USER;
|
||||
b->read_only = 0;
|
||||
b->read_only = FALSE;
|
||||
|
||||
finish:
|
||||
pa_atomic_inc(&b->pool->stat.n_allocated_by_type[b->type]);
|
||||
|
|
@ -655,7 +670,7 @@ static void memblock_replace_import(pa_memblock *b) {
|
|||
pa_mutex_unlock(seg->import->mutex);
|
||||
}
|
||||
|
||||
pa_mempool* pa_mempool_new(int shared) {
|
||||
pa_mempool* pa_mempool_new(pa_bool_t shared) {
|
||||
pa_mempool *p;
|
||||
|
||||
p = pa_xnew(pa_mempool, 1);
|
||||
|
|
@ -669,8 +684,6 @@ pa_mempool* pa_mempool_new(int shared) {
|
|||
|
||||
p->n_blocks = PA_MEMPOOL_SLOTS_MAX;
|
||||
|
||||
pa_assert(p->block_size > PA_ALIGN(sizeof(struct mempool_slot)));
|
||||
|
||||
if (pa_shm_create_rw(&p->memory, p->n_blocks * p->block_size, shared, 0700) < 0) {
|
||||
pa_xfree(p);
|
||||
return NULL;
|
||||
|
|
@ -726,7 +739,7 @@ const pa_mempool_stat* pa_mempool_get_stat(pa_mempool *p) {
|
|||
size_t pa_mempool_block_size_max(pa_mempool *p) {
|
||||
pa_assert(p);
|
||||
|
||||
return p->block_size - PA_ALIGN(sizeof(struct mempool_slot)) - PA_ALIGN(sizeof(pa_memblock));
|
||||
return p->block_size - PA_ALIGN(sizeof(pa_memblock));
|
||||
}
|
||||
|
||||
/* No lock necessary */
|
||||
|
|
@ -743,9 +756,7 @@ void pa_mempool_vacuum(pa_mempool *p) {
|
|||
;
|
||||
|
||||
while ((slot = pa_flist_pop(list))) {
|
||||
pa_shm_punch(&p->memory,
|
||||
(uint8_t*) slot - (uint8_t*) p->memory.ptr + PA_ALIGN(sizeof(struct mempool_slot)),
|
||||
p->block_size - PA_ALIGN(sizeof(struct mempool_slot)));
|
||||
pa_shm_punch(&p->memory, (uint8_t*) slot - (uint8_t*) p->memory.ptr, p->block_size);
|
||||
|
||||
while (pa_flist_push(p->free_slots, slot))
|
||||
;
|
||||
|
|
@ -767,7 +778,7 @@ int pa_mempool_get_shm_id(pa_mempool *p, uint32_t *id) {
|
|||
}
|
||||
|
||||
/* No lock necessary */
|
||||
int pa_mempool_is_shared(pa_mempool *p) {
|
||||
pa_bool_t pa_mempool_is_shared(pa_mempool *p) {
|
||||
pa_assert(p);
|
||||
|
||||
return !!p->memory.shared;
|
||||
|
|
@ -886,7 +897,8 @@ pa_memblock* pa_memimport_get(pa_memimport *i, uint32_t block_id, uint32_t shm_i
|
|||
PA_REFCNT_INIT(b);
|
||||
b->pool = i->pool;
|
||||
b->type = PA_MEMBLOCK_IMPORTED;
|
||||
b->read_only = 1;
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -87,13 +87,13 @@ pa_memblock *pa_memblock_new(pa_mempool *, size_t length);
|
|||
pa_memblock *pa_memblock_new_pool(pa_mempool *, size_t length);
|
||||
|
||||
/* Allocate a new memory block of type PA_MEMBLOCK_USER */
|
||||
pa_memblock *pa_memblock_new_user(pa_mempool *, void *data, size_t length, void (*free_cb)(void *p), int read_only);
|
||||
pa_memblock *pa_memblock_new_user(pa_mempool *, void *data, size_t length, pa_free_cb_t free_cb, pa_bool_t read_only);
|
||||
|
||||
/* A special case of pa_memblock_new_user: take a memory buffer previously allocated with pa_xmalloc() */
|
||||
#define pa_memblock_new_malloced(p,data,length) pa_memblock_new_user(p, data, length, pa_xfree, 0)
|
||||
|
||||
/* Allocate a new memory block of type PA_MEMBLOCK_FIXED */
|
||||
pa_memblock *pa_memblock_new_fixed(pa_mempool *, void *data, size_t length, int read_only);
|
||||
pa_memblock *pa_memblock_new_fixed(pa_mempool *, void *data, size_t length, pa_bool_t read_only);
|
||||
|
||||
void pa_memblock_unref(pa_memblock*b);
|
||||
pa_memblock* pa_memblock_ref(pa_memblock*b);
|
||||
|
|
@ -106,8 +106,11 @@ function is not multiple caller safe, i.e. needs to be locked
|
|||
manually if called from more than one thread at the same time. */
|
||||
void pa_memblock_unref_fixed(pa_memblock*b);
|
||||
|
||||
int pa_memblock_is_read_only(pa_memblock *b);
|
||||
int pa_memblock_ref_is_one(pa_memblock *b);
|
||||
pa_bool_t pa_memblock_is_read_only(pa_memblock *b);
|
||||
pa_bool_t pa_memblock_is_silence(pa_memblock *b);
|
||||
pa_bool_t pa_memblock_ref_is_one(pa_memblock *b);
|
||||
void pa_memblock_set_is_silence(pa_memblock *b, pa_bool_t v);
|
||||
|
||||
void* pa_memblock_acquire(pa_memblock *b);
|
||||
void pa_memblock_release(pa_memblock *b);
|
||||
size_t pa_memblock_get_length(pa_memblock *b);
|
||||
|
|
@ -116,12 +119,12 @@ pa_mempool * pa_memblock_get_pool(pa_memblock *b);
|
|||
pa_memblock *pa_memblock_will_need(pa_memblock *b);
|
||||
|
||||
/* The memory block manager */
|
||||
pa_mempool* pa_mempool_new(int shared);
|
||||
pa_mempool* pa_mempool_new(pa_bool_t shared);
|
||||
void pa_mempool_free(pa_mempool *p);
|
||||
const pa_mempool_stat* pa_mempool_get_stat(pa_mempool *p);
|
||||
void pa_mempool_vacuum(pa_mempool *p);
|
||||
int pa_mempool_get_shm_id(pa_mempool *p, uint32_t *id);
|
||||
int pa_mempool_is_shared(pa_mempool *p);
|
||||
pa_bool_t pa_mempool_is_shared(pa_mempool *p);
|
||||
size_t pa_mempool_block_size_max(pa_mempool *p);
|
||||
|
||||
/* For recieving blocks from other nodes */
|
||||
|
|
|
|||
|
|
@ -50,11 +50,12 @@ PA_STATIC_FLIST_DECLARE(list_items, 0, pa_xfree);
|
|||
|
||||
struct pa_memblockq {
|
||||
struct list_item *blocks, *blocks_tail;
|
||||
struct list_item *current_read, *current_write;
|
||||
unsigned n_blocks;
|
||||
size_t maxlength, tlength, base, prebuf, minreq;
|
||||
size_t maxlength, tlength, base, prebuf, minreq, maxrewind;
|
||||
int64_t read_index, write_index;
|
||||
pa_bool_t in_prebuf;
|
||||
pa_memblock *silence;
|
||||
pa_memchunk silence;
|
||||
pa_mcalign *mcalign;
|
||||
int64_t missing;
|
||||
size_t requested;
|
||||
|
|
@ -67,7 +68,8 @@ pa_memblockq* pa_memblockq_new(
|
|||
size_t base,
|
||||
size_t prebuf,
|
||||
size_t minreq,
|
||||
pa_memblock *silence) {
|
||||
size_t maxrewind,
|
||||
pa_memchunk *silence) {
|
||||
|
||||
pa_memblockq* bq;
|
||||
|
||||
|
|
@ -75,27 +77,34 @@ pa_memblockq* pa_memblockq_new(
|
|||
|
||||
bq = pa_xnew(pa_memblockq, 1);
|
||||
bq->blocks = bq->blocks_tail = NULL;
|
||||
bq->current_read = bq->current_write = NULL;
|
||||
bq->n_blocks = 0;
|
||||
|
||||
bq->base = base;
|
||||
bq->read_index = bq->write_index = idx;
|
||||
|
||||
pa_log_debug("memblockq requested: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu",
|
||||
(unsigned long) maxlength, (unsigned long) tlength, (unsigned long) base, (unsigned long) prebuf, (unsigned long) minreq);
|
||||
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) base, (unsigned long) prebuf, (unsigned long) minreq, (unsigned long) maxrewind);
|
||||
|
||||
bq->missing = bq->requested = bq->maxlength = bq->tlength = bq->prebuf = bq->minreq = 0;
|
||||
bq->missing = bq->requested = bq->maxlength = bq->tlength = bq->prebuf = bq->minreq = bq->maxrewind = 0;
|
||||
bq->in_prebuf = TRUE;
|
||||
|
||||
pa_memblockq_set_maxlength(bq, maxlength);
|
||||
pa_memblockq_set_tlength(bq, tlength);
|
||||
pa_memblockq_set_prebuf(bq, prebuf);
|
||||
pa_memblockq_set_minreq(bq, minreq);
|
||||
pa_memblockq_set_maxrewind(bq, maxrewind);
|
||||
|
||||
pa_log_debug("memblockq sanitized: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu",
|
||||
(unsigned long)bq->maxlength, (unsigned long)bq->tlength, (unsigned long)bq->base, (unsigned long)bq->prebuf, (unsigned long)bq->minreq);
|
||||
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);
|
||||
|
||||
bq->silence = silence ? pa_memblock_ref(silence) : NULL;
|
||||
bq->mcalign = NULL;
|
||||
if (silence) {
|
||||
bq->silence = *silence;
|
||||
pa_memblock_ref(bq->silence.memblock);
|
||||
} else
|
||||
pa_memchunk_reset(&bq->silence);
|
||||
|
||||
bq->mcalign = pa_mcalign_new(bq->base);
|
||||
|
||||
return bq;
|
||||
}
|
||||
|
|
@ -105,8 +114,8 @@ void pa_memblockq_free(pa_memblockq* bq) {
|
|||
|
||||
pa_memblockq_flush(bq);
|
||||
|
||||
if (bq->silence)
|
||||
pa_memblock_unref(bq->silence);
|
||||
if (bq->silence.memblock)
|
||||
pa_memblock_unref(bq->silence.memblock);
|
||||
|
||||
if (bq->mcalign)
|
||||
pa_mcalign_free(bq->mcalign);
|
||||
|
|
@ -114,6 +123,62 @@ void pa_memblockq_free(pa_memblockq* bq) {
|
|||
pa_xfree(bq);
|
||||
}
|
||||
|
||||
static void fix_current_read(pa_memblockq *bq) {
|
||||
pa_assert(bq);
|
||||
|
||||
if (PA_UNLIKELY(!bq->blocks)) {
|
||||
bq->current_read = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
if (PA_UNLIKELY(!bq->current_read))
|
||||
bq->current_read = bq->blocks;
|
||||
|
||||
/* Scan left */
|
||||
while (PA_UNLIKELY(bq->current_read->index > bq->read_index))
|
||||
|
||||
if (bq->current_read->prev)
|
||||
bq->current_read = bq->current_read->prev;
|
||||
else
|
||||
break;
|
||||
|
||||
/* Scan right */
|
||||
while (PA_LIKELY(bq->current_read != NULL) && PA_UNLIKELY(bq->current_read->index + (int64_t) bq->current_read->chunk.length <= bq->read_index))
|
||||
bq->current_read = bq->current_read->next;
|
||||
|
||||
/* At this point current_read will either point at or left of the
|
||||
next block to play. It may be NULL in case everything in
|
||||
the queue was already played */
|
||||
}
|
||||
|
||||
static void fix_current_write(pa_memblockq *bq) {
|
||||
pa_assert(bq);
|
||||
|
||||
if (PA_UNLIKELY(!bq->blocks)) {
|
||||
bq->current_write = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
if (PA_UNLIKELY(!bq->current_write))
|
||||
bq->current_write = bq->blocks_tail;
|
||||
|
||||
/* Scan right */
|
||||
while (PA_UNLIKELY(bq->current_write->index + (int64_t) bq->current_write->chunk.length <= bq->write_index))
|
||||
|
||||
if (bq->current_write->next)
|
||||
bq->current_write = bq->current_write->next;
|
||||
else
|
||||
break;
|
||||
|
||||
/* Scan left */
|
||||
while (PA_LIKELY(bq->current_write != NULL) && PA_UNLIKELY(bq->current_write->index > bq->write_index))
|
||||
bq->current_write = bq->current_write->prev;
|
||||
|
||||
/* At this point current_write will either point at or right of
|
||||
the next block to write data to. It may be NULL in case
|
||||
everything in the queue is still to be played */
|
||||
}
|
||||
|
||||
static void drop_block(pa_memblockq *bq, struct list_item *q) {
|
||||
pa_assert(bq);
|
||||
pa_assert(q);
|
||||
|
|
@ -122,13 +187,23 @@ static void drop_block(pa_memblockq *bq, struct list_item *q) {
|
|||
|
||||
if (q->prev)
|
||||
q->prev->next = q->next;
|
||||
else
|
||||
else {
|
||||
pa_assert(bq->blocks == q);
|
||||
bq->blocks = q->next;
|
||||
}
|
||||
|
||||
if (q->next)
|
||||
q->next->prev = q->prev;
|
||||
else
|
||||
else {
|
||||
pa_assert(bq->blocks_tail == q);
|
||||
bq->blocks_tail = q->prev;
|
||||
}
|
||||
|
||||
if (bq->current_write == q)
|
||||
bq->current_write = q->prev;
|
||||
|
||||
if (bq->current_read == q)
|
||||
bq->current_read = q->next;
|
||||
|
||||
pa_memblock_unref(q->chunk.memblock);
|
||||
|
||||
|
|
@ -138,6 +213,16 @@ static void drop_block(pa_memblockq *bq, struct list_item *q) {
|
|||
bq->n_blocks--;
|
||||
}
|
||||
|
||||
static void drop_backlog(pa_memblockq *bq) {
|
||||
int64_t boundary;
|
||||
pa_assert(bq);
|
||||
|
||||
boundary = bq->read_index - bq->maxrewind;
|
||||
|
||||
while (bq->blocks && (bq->blocks->index + (int64_t) bq->blocks->chunk.length <= boundary))
|
||||
drop_block(bq, bq->blocks);
|
||||
}
|
||||
|
||||
static pa_bool_t can_push(pa_memblockq *bq, size_t l) {
|
||||
int64_t end;
|
||||
|
||||
|
|
@ -152,10 +237,10 @@ static pa_bool_t can_push(pa_memblockq *bq, size_t l) {
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
end = bq->blocks_tail ? bq->blocks_tail->index + bq->blocks_tail->chunk.length : 0;
|
||||
end = bq->blocks_tail ? bq->blocks_tail->index + (int64_t) bq->blocks_tail->chunk.length : bq->write_index;
|
||||
|
||||
/* Make sure that the list doesn't get too long */
|
||||
if (bq->write_index + (int64_t)l > end)
|
||||
if (bq->write_index + (int64_t) l > end)
|
||||
if (bq->write_index + l - bq->read_index > bq->maxlength)
|
||||
return FALSE;
|
||||
|
||||
|
|
@ -182,28 +267,26 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {
|
|||
old = bq->write_index;
|
||||
chunk = *uchunk;
|
||||
|
||||
if (bq->read_index > bq->write_index) {
|
||||
fix_current_write(bq);
|
||||
q = bq->current_write;
|
||||
|
||||
/* We currently have a buffer underflow, we need to drop some
|
||||
* incoming data */
|
||||
/* First we advance the q pointer right of where we want to
|
||||
* write to */
|
||||
|
||||
size_t d = bq->read_index - bq->write_index;
|
||||
|
||||
if (chunk.length > d) {
|
||||
chunk.index += d;
|
||||
chunk.length -= d;
|
||||
bq->write_index += d;
|
||||
} else {
|
||||
/* We drop the incoming data completely */
|
||||
bq->write_index += chunk.length;
|
||||
goto finish;
|
||||
}
|
||||
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 */
|
||||
|
||||
q = bq->blocks_tail;
|
||||
while (q) {
|
||||
|
||||
if (bq->write_index >= q->index + (int64_t) q->chunk.length)
|
||||
|
|
@ -329,7 +412,7 @@ finish:
|
|||
|
||||
delta = bq->write_index - old;
|
||||
|
||||
if (delta >= bq->requested) {
|
||||
if (delta >= (int64_t) bq->requested) {
|
||||
delta -= bq->requested;
|
||||
bq->requested = 0;
|
||||
} else {
|
||||
|
|
@ -342,7 +425,16 @@ finish:
|
|||
return 0;
|
||||
}
|
||||
|
||||
static pa_bool_t memblockq_check_prebuf(pa_memblockq *bq) {
|
||||
pa_bool_t pa_memblockq_prebuf_active(pa_memblockq *bq) {
|
||||
pa_assert(bq);
|
||||
|
||||
if (bq->in_prebuf)
|
||||
return pa_memblockq_get_length(bq) < bq->prebuf;
|
||||
else
|
||||
return bq->prebuf > 0 && bq->read_index >= bq->write_index;
|
||||
}
|
||||
|
||||
static pa_bool_t update_prebuf(pa_memblockq *bq) {
|
||||
pa_assert(bq);
|
||||
|
||||
if (bq->in_prebuf) {
|
||||
|
|
@ -364,34 +456,42 @@ static pa_bool_t memblockq_check_prebuf(pa_memblockq *bq) {
|
|||
}
|
||||
|
||||
int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk) {
|
||||
int64_t d;
|
||||
pa_assert(bq);
|
||||
pa_assert(chunk);
|
||||
|
||||
/* We need to pre-buffer */
|
||||
if (memblockq_check_prebuf(bq))
|
||||
if (update_prebuf(bq))
|
||||
return -1;
|
||||
|
||||
fix_current_read(bq);
|
||||
|
||||
/* Do we need to spit out silence? */
|
||||
if (!bq->blocks || bq->blocks->index > bq->read_index) {
|
||||
if (!bq->current_read || bq->current_read->index > bq->read_index) {
|
||||
|
||||
size_t length;
|
||||
|
||||
/* How much silence shall we return? */
|
||||
length = bq->blocks ? bq->blocks->index - bq->read_index : 0;
|
||||
if (bq->current_read)
|
||||
length = bq->current_read->index - bq->read_index;
|
||||
else if (bq->write_index > bq->read_index)
|
||||
length = (size_t) (bq->write_index - bq->read_index);
|
||||
else
|
||||
length = 0;
|
||||
|
||||
/* We need to return silence, since no data is yet available */
|
||||
if (bq->silence) {
|
||||
chunk->memblock = pa_memblock_ref(bq->silence);
|
||||
if (bq->silence.memblock) {
|
||||
*chunk = bq->silence;
|
||||
pa_memblock_ref(chunk->memblock);
|
||||
|
||||
if (!length || length > pa_memblock_get_length(chunk->memblock))
|
||||
length = pa_memblock_get_length(chunk->memblock);
|
||||
if (length > 0 && length < chunk->length)
|
||||
chunk->length = length;
|
||||
|
||||
chunk->length = length;
|
||||
} else {
|
||||
|
||||
/* If the memblockq is empty, return -1, otherwise return
|
||||
* the time to sleep */
|
||||
if (!bq->blocks)
|
||||
if (length <= 0)
|
||||
return -1;
|
||||
|
||||
chunk->memblock = NULL;
|
||||
|
|
@ -403,11 +503,14 @@ int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk) {
|
|||
}
|
||||
|
||||
/* Ok, let's pass real data to the caller */
|
||||
pa_assert(bq->blocks->index == bq->read_index);
|
||||
|
||||
*chunk = bq->blocks->chunk;
|
||||
*chunk = bq->current_read->chunk;
|
||||
pa_memblock_ref(chunk->memblock);
|
||||
|
||||
pa_assert(bq->read_index >= bq->current_read->index);
|
||||
d = bq->read_index - bq->current_read->index;
|
||||
chunk->index += d;
|
||||
chunk->length -= d;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -421,45 +524,26 @@ void pa_memblockq_drop(pa_memblockq *bq, size_t length) {
|
|||
while (length > 0) {
|
||||
|
||||
/* Do not drop any data when we are in prebuffering mode */
|
||||
if (memblockq_check_prebuf(bq))
|
||||
if (update_prebuf(bq))
|
||||
break;
|
||||
|
||||
if (bq->blocks) {
|
||||
size_t d;
|
||||
fix_current_read(bq);
|
||||
|
||||
pa_assert(bq->blocks->index >= bq->read_index);
|
||||
if (bq->current_read) {
|
||||
int64_t p, d;
|
||||
|
||||
d = (size_t) (bq->blocks->index - bq->read_index);
|
||||
/* We go through this piece by piece to make sure we don't
|
||||
* drop more than allowed by prebuf */
|
||||
|
||||
if (d >= length) {
|
||||
/* The first block is too far in the future */
|
||||
p = bq->current_read->index + bq->current_read->chunk.length;
|
||||
pa_assert(p >= bq->read_index);
|
||||
d = p - bq->read_index;
|
||||
|
||||
bq->read_index += length;
|
||||
break;
|
||||
} else {
|
||||
if (d > (int64_t) length)
|
||||
d = length;
|
||||
|
||||
length -= d;
|
||||
bq->read_index += d;
|
||||
}
|
||||
|
||||
pa_assert(bq->blocks->index == bq->read_index);
|
||||
|
||||
if (bq->blocks->chunk.length <= length) {
|
||||
/* We need to drop the full block */
|
||||
|
||||
length -= bq->blocks->chunk.length;
|
||||
bq->read_index += bq->blocks->chunk.length;
|
||||
|
||||
drop_block(bq, bq->blocks);
|
||||
} else {
|
||||
/* Only the start of this block needs to be dropped */
|
||||
|
||||
bq->blocks->chunk.index += length;
|
||||
bq->blocks->chunk.length -= length;
|
||||
bq->blocks->index += length;
|
||||
bq->read_index += length;
|
||||
break;
|
||||
}
|
||||
bq->read_index += d;
|
||||
length -= d;
|
||||
|
||||
} else {
|
||||
|
||||
|
|
@ -469,20 +553,32 @@ void pa_memblockq_drop(pa_memblockq *bq, size_t length) {
|
|||
}
|
||||
}
|
||||
|
||||
drop_backlog(bq);
|
||||
|
||||
delta = bq->read_index - old;
|
||||
bq->missing += delta;
|
||||
}
|
||||
|
||||
int pa_memblockq_is_readable(pa_memblockq *bq) {
|
||||
void pa_memblockq_rewind(pa_memblockq *bq, size_t length) {
|
||||
pa_assert(bq);
|
||||
pa_assert(length % bq->base == 0);
|
||||
|
||||
/* This is kind of the inverse of pa_memblockq_drop() */
|
||||
|
||||
bq->read_index -= length;
|
||||
bq->missing -= length;
|
||||
}
|
||||
|
||||
pa_bool_t pa_memblockq_is_readable(pa_memblockq *bq) {
|
||||
pa_assert(bq);
|
||||
|
||||
if (memblockq_check_prebuf(bq))
|
||||
return 0;
|
||||
if (pa_memblockq_prebuf_active(bq))
|
||||
return FALSE;
|
||||
|
||||
if (pa_memblockq_get_length(bq) <= 0)
|
||||
return 0;
|
||||
return FALSE;
|
||||
|
||||
return 1;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
size_t pa_memblockq_get_length(pa_memblockq *bq) {
|
||||
|
|
@ -506,12 +602,6 @@ size_t pa_memblockq_missing(pa_memblockq *bq) {
|
|||
return l >= bq->minreq ? l : 0;
|
||||
}
|
||||
|
||||
size_t pa_memblockq_get_minreq(pa_memblockq *bq) {
|
||||
pa_assert(bq);
|
||||
|
||||
return bq->minreq;
|
||||
}
|
||||
|
||||
void pa_memblockq_seek(pa_memblockq *bq, int64_t offset, pa_seek_mode_t seek) {
|
||||
int64_t old, delta;
|
||||
pa_assert(bq);
|
||||
|
|
@ -535,9 +625,11 @@ void pa_memblockq_seek(pa_memblockq *bq, int64_t offset, pa_seek_mode_t seek) {
|
|||
pa_assert_not_reached();
|
||||
}
|
||||
|
||||
drop_backlog(bq);
|
||||
|
||||
delta = bq->write_index - old;
|
||||
|
||||
if (delta >= bq->requested) {
|
||||
if (delta >= (int64_t) bq->requested) {
|
||||
delta -= bq->requested;
|
||||
bq->requested = 0;
|
||||
} else if (delta >= 0) {
|
||||
|
|
@ -552,10 +644,7 @@ void pa_memblockq_flush(pa_memblockq *bq) {
|
|||
int64_t old, delta;
|
||||
pa_assert(bq);
|
||||
|
||||
while (bq->blocks)
|
||||
drop_block(bq, bq->blocks);
|
||||
|
||||
pa_assert(bq->n_blocks == 0);
|
||||
pa_memblockq_silence(bq);
|
||||
|
||||
old = bq->write_index;
|
||||
bq->write_index = bq->read_index;
|
||||
|
|
@ -564,7 +653,7 @@ void pa_memblockq_flush(pa_memblockq *bq) {
|
|||
|
||||
delta = bq->write_index - old;
|
||||
|
||||
if (delta > bq->requested) {
|
||||
if (delta >= (int64_t) bq->requested) {
|
||||
delta -= bq->requested;
|
||||
bq->requested = 0;
|
||||
} else if (delta >= 0) {
|
||||
|
|
@ -581,13 +670,21 @@ size_t pa_memblockq_get_tlength(pa_memblockq *bq) {
|
|||
return bq->tlength;
|
||||
}
|
||||
|
||||
size_t pa_memblockq_get_minreq(pa_memblockq *bq) {
|
||||
pa_assert(bq);
|
||||
|
||||
return bq->minreq;
|
||||
}
|
||||
|
||||
int64_t pa_memblockq_get_read_index(pa_memblockq *bq) {
|
||||
pa_assert(bq);
|
||||
|
||||
return bq->read_index;
|
||||
}
|
||||
|
||||
int64_t pa_memblockq_get_write_index(pa_memblockq *bq) {
|
||||
pa_assert(bq);
|
||||
|
||||
return bq->write_index;
|
||||
}
|
||||
|
||||
|
|
@ -600,9 +697,6 @@ int pa_memblockq_push_align(pa_memblockq* bq, const pa_memchunk *chunk) {
|
|||
if (bq->base == 1)
|
||||
return pa_memblockq_push(bq, chunk);
|
||||
|
||||
if (!bq->mcalign)
|
||||
bq->mcalign = pa_mcalign_new(bq->base);
|
||||
|
||||
if (!can_push(bq, pa_mcalign_csize(bq->mcalign, chunk->length)))
|
||||
return -1;
|
||||
|
||||
|
|
@ -613,23 +707,15 @@ int pa_memblockq_push_align(pa_memblockq* bq, const pa_memchunk *chunk) {
|
|||
r = pa_memblockq_push(bq, &rchunk);
|
||||
pa_memblock_unref(rchunk.memblock);
|
||||
|
||||
if (r < 0)
|
||||
if (r < 0) {
|
||||
pa_mcalign_flush(bq->mcalign);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pa_memblockq_shorten(pa_memblockq *bq, size_t length) {
|
||||
size_t l;
|
||||
pa_assert(bq);
|
||||
|
||||
l = pa_memblockq_get_length(bq);
|
||||
|
||||
if (l > length)
|
||||
pa_memblockq_drop(bq, l - length);
|
||||
}
|
||||
|
||||
void pa_memblockq_prebuf_disable(pa_memblockq *bq) {
|
||||
pa_assert(bq);
|
||||
|
||||
|
|
@ -639,7 +725,7 @@ void pa_memblockq_prebuf_disable(pa_memblockq *bq) {
|
|||
void pa_memblockq_prebuf_force(pa_memblockq *bq) {
|
||||
pa_assert(bq);
|
||||
|
||||
if (!bq->in_prebuf && bq->prebuf > 0)
|
||||
if (bq->prebuf > 0)
|
||||
bq->in_prebuf = TRUE;
|
||||
}
|
||||
|
||||
|
|
@ -691,18 +777,17 @@ void pa_memblockq_set_tlength(pa_memblockq *bq, size_t tlength) {
|
|||
size_t old_tlength;
|
||||
pa_assert(bq);
|
||||
|
||||
old_tlength = bq->tlength;
|
||||
|
||||
if (tlength <= 0)
|
||||
tlength = bq->maxlength;
|
||||
|
||||
old_tlength = bq->tlength;
|
||||
bq->tlength = ((tlength+bq->base-1)/bq->base)*bq->base;
|
||||
|
||||
if (bq->tlength > bq->maxlength)
|
||||
bq->tlength = bq->maxlength;
|
||||
|
||||
if (bq->minreq > bq->tlength - bq->prebuf)
|
||||
pa_memblockq_set_minreq(bq, bq->tlength - bq->prebuf);
|
||||
if (bq->minreq > bq->tlength)
|
||||
pa_memblockq_set_minreq(bq, bq->tlength);
|
||||
|
||||
bq->missing += (int64_t) bq->tlength - (int64_t) old_tlength;
|
||||
}
|
||||
|
|
@ -710,8 +795,10 @@ void pa_memblockq_set_tlength(pa_memblockq *bq, size_t tlength) {
|
|||
void pa_memblockq_set_prebuf(pa_memblockq *bq, size_t prebuf) {
|
||||
pa_assert(bq);
|
||||
|
||||
bq->prebuf = (prebuf == (size_t) -1) ? bq->tlength/2 : prebuf;
|
||||
bq->prebuf = ((bq->prebuf+bq->base-1)/bq->base)*bq->base;
|
||||
if (prebuf == (size_t) -1)
|
||||
prebuf = bq->tlength;
|
||||
|
||||
bq->prebuf = ((prebuf+bq->base-1)/bq->base)*bq->base;
|
||||
|
||||
if (prebuf > 0 && bq->prebuf < bq->base)
|
||||
bq->prebuf = bq->base;
|
||||
|
|
@ -722,8 +809,8 @@ void pa_memblockq_set_prebuf(pa_memblockq *bq, size_t prebuf) {
|
|||
if (bq->prebuf <= 0 || pa_memblockq_get_length(bq) >= bq->prebuf)
|
||||
bq->in_prebuf = FALSE;
|
||||
|
||||
if (bq->minreq > bq->tlength - bq->prebuf)
|
||||
pa_memblockq_set_minreq(bq, bq->tlength - bq->prebuf);
|
||||
if (bq->minreq > bq->prebuf)
|
||||
pa_memblockq_set_minreq(bq, bq->prebuf);
|
||||
}
|
||||
|
||||
void pa_memblockq_set_minreq(pa_memblockq *bq, size_t minreq) {
|
||||
|
|
@ -731,9 +818,93 @@ void pa_memblockq_set_minreq(pa_memblockq *bq, size_t minreq) {
|
|||
|
||||
bq->minreq = (minreq/bq->base)*bq->base;
|
||||
|
||||
if (bq->minreq > bq->tlength - bq->prebuf)
|
||||
bq->minreq = bq->tlength - bq->prebuf;
|
||||
if (bq->minreq > bq->tlength)
|
||||
bq->minreq = bq->tlength;
|
||||
|
||||
if (bq->minreq > bq->prebuf)
|
||||
bq->minreq = bq->prebuf;
|
||||
|
||||
if (bq->minreq < bq->base)
|
||||
bq->minreq = bq->base;
|
||||
}
|
||||
|
||||
void pa_memblockq_set_maxrewind(pa_memblockq *bq, size_t maxrewind) {
|
||||
pa_assert(bq);
|
||||
|
||||
bq->maxrewind = (maxrewind/bq->base)*bq->base;
|
||||
}
|
||||
|
||||
int pa_memblockq_splice(pa_memblockq *bq, pa_memblockq *source) {
|
||||
|
||||
pa_assert(bq);
|
||||
pa_assert(source);
|
||||
|
||||
pa_memblockq_prebuf_disable(bq);
|
||||
|
||||
for (;;) {
|
||||
pa_memchunk chunk;
|
||||
|
||||
if (pa_memblockq_peek(source, &chunk) < 0)
|
||||
return 0;
|
||||
|
||||
pa_assert(chunk.length > 0);
|
||||
|
||||
if (chunk.memblock) {
|
||||
|
||||
if (pa_memblockq_push_align(bq, &chunk) < 0) {
|
||||
pa_memblock_unref(chunk.memblock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pa_memblock_unref(chunk.memblock);
|
||||
} else
|
||||
pa_memblockq_seek(bq, chunk.length, PA_SEEK_RELATIVE);
|
||||
|
||||
pa_memblockq_drop(bq, chunk.length);
|
||||
}
|
||||
}
|
||||
|
||||
void pa_memblockq_willneed(pa_memblockq *bq) {
|
||||
struct list_item *q;
|
||||
|
||||
pa_assert(bq);
|
||||
|
||||
fix_current_read(bq);
|
||||
|
||||
for (q = bq->current_read; q; q = q->next)
|
||||
pa_memchunk_will_need(&q->chunk);
|
||||
}
|
||||
|
||||
void pa_memblockq_set_silence(pa_memblockq *bq, pa_memchunk *silence) {
|
||||
pa_assert(bq);
|
||||
|
||||
if (bq->silence.memblock)
|
||||
pa_memblock_unref(bq->silence.memblock);
|
||||
|
||||
if (silence) {
|
||||
bq->silence = *silence;
|
||||
pa_memblock_ref(bq->silence.memblock);
|
||||
} else
|
||||
pa_memchunk_reset(&bq->silence);
|
||||
}
|
||||
|
||||
pa_bool_t pa_memblockq_is_empty(pa_memblockq *bq) {
|
||||
pa_assert(bq);
|
||||
|
||||
return !bq->blocks;
|
||||
}
|
||||
|
||||
void pa_memblockq_silence(pa_memblockq *bq) {
|
||||
pa_assert(bq);
|
||||
|
||||
while (bq->blocks)
|
||||
drop_block(bq, bq->blocks);
|
||||
|
||||
pa_assert(bq->n_blocks == 0);
|
||||
}
|
||||
|
||||
unsigned pa_memblockq_get_nblocks(pa_memblockq *bq) {
|
||||
pa_assert(bq);
|
||||
|
||||
return bq->n_blocks;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,7 +62,9 @@ typedef struct pa_memblockq pa_memblockq;
|
|||
- minreq: pa_memblockq_missing() will only return values greater
|
||||
than this value. Pass 0 for the default.
|
||||
|
||||
- silence: return this memblock when reading unitialized data
|
||||
- maxrewind: how many bytes of history to keep in the queue
|
||||
|
||||
- silence: return this memchunk when reading unitialized data
|
||||
*/
|
||||
pa_memblockq* pa_memblockq_new(
|
||||
int64_t idx,
|
||||
|
|
@ -71,7 +73,8 @@ pa_memblockq* pa_memblockq_new(
|
|||
size_t base,
|
||||
size_t prebuf,
|
||||
size_t minreq,
|
||||
pa_memblock *silence);
|
||||
size_t maxrewind,
|
||||
pa_memchunk *silence);
|
||||
|
||||
void pa_memblockq_free(pa_memblockq*bq);
|
||||
|
||||
|
|
@ -95,7 +98,7 @@ int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk);
|
|||
void pa_memblockq_drop(pa_memblockq *bq, size_t length);
|
||||
|
||||
/* Test if the pa_memblockq is currently readable, that is, more data than base */
|
||||
int pa_memblockq_is_readable(pa_memblockq *bq);
|
||||
pa_bool_t pa_memblockq_is_readable(pa_memblockq *bq);
|
||||
|
||||
/* Return the length of the queue in bytes */
|
||||
size_t pa_memblockq_get_length(pa_memblockq *bq);
|
||||
|
|
@ -107,6 +110,9 @@ size_t pa_memblockq_missing(pa_memblockq *bq);
|
|||
* this function, reset the internal counter to 0. */
|
||||
size_t pa_memblockq_pop_missing(pa_memblockq *bq);
|
||||
|
||||
/* Directly moves the data from the source memblockq into bq */
|
||||
int pa_memblockq_splice(pa_memblockq *bq, pa_memblockq *source);
|
||||
|
||||
/* Returns the minimal request value */
|
||||
size_t pa_memblockq_get_minreq(pa_memblockq *bq);
|
||||
|
||||
|
|
@ -125,10 +131,8 @@ int64_t pa_memblockq_get_read_index(pa_memblockq *bq);
|
|||
/* Return the current write index */
|
||||
int64_t pa_memblockq_get_write_index(pa_memblockq *bq);
|
||||
|
||||
/* Shorten the pa_memblockq to the specified length by dropping data
|
||||
* at the read end of the queue. The read index is increased until the
|
||||
* queue has the specified length */
|
||||
void pa_memblockq_shorten(pa_memblockq *bq, size_t length);
|
||||
/* Rewind the read index. If the history is shorter than the specified length we'll point to silence afterwards. */
|
||||
void pa_memblockq_rewind(pa_memblockq *bq, size_t length);
|
||||
|
||||
/* Ignore prebuf for now */
|
||||
void pa_memblockq_prebuf_disable(pa_memblockq *bq);
|
||||
|
|
@ -142,10 +146,27 @@ size_t pa_memblockq_get_maxlength(pa_memblockq *bq);
|
|||
/* Return the prebuffer length in bytes */
|
||||
size_t pa_memblockq_get_prebuf(pa_memblockq *bq);
|
||||
|
||||
/* Change metrics. */
|
||||
void pa_memblockq_set_maxlength(pa_memblockq *memblockq, size_t maxlength);
|
||||
void pa_memblockq_set_tlength(pa_memblockq *memblockq, size_t tlength);
|
||||
void pa_memblockq_set_prebuf(pa_memblockq *memblockq, size_t prebuf);
|
||||
/* Change metrics. Always call in order. */
|
||||
void pa_memblockq_set_maxlength(pa_memblockq *memblockq, size_t maxlength); /* might modify tlength, prebuf, minreq too */
|
||||
void pa_memblockq_set_tlength(pa_memblockq *memblockq, size_t tlength); /* might modify minreq, too */
|
||||
void pa_memblockq_set_prebuf(pa_memblockq *memblockq, size_t prebuf); /* might modify minreq, too */
|
||||
void pa_memblockq_set_minreq(pa_memblockq *memblockq, size_t minreq);
|
||||
void pa_memblockq_set_maxrewind(pa_memblockq *memblockq, size_t rewind); /* Set the maximum history size */
|
||||
void pa_memblockq_set_silence(pa_memblockq *memblockq, pa_memchunk *silence);
|
||||
|
||||
/* Call pa_memchunk_willneed() for every chunk in the queue from the current read pointer to the end */
|
||||
void pa_memblockq_willneed(pa_memblockq *bq);
|
||||
|
||||
/* Check whether the memblockq is completely empty, i.e. no data
|
||||
* neither left nor right of the read pointer, and hence no buffered
|
||||
* data for the future nor data in the backlog. */
|
||||
pa_bool_t pa_memblockq_is_empty(pa_memblockq *bq);
|
||||
|
||||
void pa_memblockq_silence(pa_memblockq *bq);
|
||||
|
||||
/* Check whether we currently are in prebuf state */
|
||||
pa_bool_t pa_memblockq_prebuf_active(pa_memblockq *bq);
|
||||
|
||||
unsigned pa_memblockq_get_nblocks(pa_memblockq *bq);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -90,3 +90,23 @@ pa_memchunk *pa_memchunk_will_need(const pa_memchunk *c) {
|
|||
|
||||
return (pa_memchunk*) c;
|
||||
}
|
||||
|
||||
pa_memchunk* pa_memchunk_memcpy(pa_memchunk *dst, pa_memchunk *src) {
|
||||
void *p, *q;
|
||||
|
||||
pa_assert(dst);
|
||||
pa_assert(src);
|
||||
pa_assert(dst->length == src->length);
|
||||
|
||||
p = pa_memblock_acquire(dst->memblock);
|
||||
q = pa_memblock_acquire(src->memblock);
|
||||
|
||||
memmove((uint8_t*) p + dst->index,
|
||||
(uint8_t*) q + src->index,
|
||||
dst->length);
|
||||
|
||||
pa_memblock_release(dst->memblock);
|
||||
pa_memblock_release(src->memblock);
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,4 +49,7 @@ pa_memchunk* pa_memchunk_reset(pa_memchunk *c);
|
|||
/* Map a memory chunk back into memory if it was swapped out */
|
||||
pa_memchunk *pa_memchunk_will_need(const pa_memchunk *c);
|
||||
|
||||
/* Copy the data in the src memchunk to the dst memchunk */
|
||||
pa_memchunk* pa_memchunk_memcpy(pa_memchunk *dst, pa_memchunk *src);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -109,8 +109,8 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
|
|||
m->userdata = NULL;
|
||||
m->core = c;
|
||||
m->n_used = -1;
|
||||
m->auto_unload = 0;
|
||||
m->unload_requested = 0;
|
||||
m->auto_unload = FALSE;
|
||||
m->unload_requested = FALSE;
|
||||
|
||||
if (m->init(m) < 0) {
|
||||
pa_log_error("Failed to load module \"%s\" (argument: \"%s\"): initialization failed.", name, argument ? argument : "");
|
||||
|
|
@ -281,7 +281,7 @@ static void defer_cb(pa_mainloop_api*api, pa_defer_event *e, void *userdata) {
|
|||
void pa_module_unload_request(pa_module *m) {
|
||||
pa_assert(m);
|
||||
|
||||
m->unload_requested = 1;
|
||||
m->unload_requested = TRUE;
|
||||
|
||||
if (!m->core->module_defer_unload_event)
|
||||
m->core->module_defer_unload_event = m->core->mainloop->defer_new(m->core->mainloop, defer_cb, m->core);
|
||||
|
|
|
|||
|
|
@ -45,10 +45,10 @@ struct pa_module {
|
|||
void *userdata;
|
||||
|
||||
int n_used;
|
||||
int auto_unload;
|
||||
pa_bool_t auto_unload;
|
||||
time_t last_used_time;
|
||||
|
||||
int unload_requested;
|
||||
pa_bool_t unload_requested;
|
||||
};
|
||||
|
||||
pa_module* pa_module_load(pa_core *c, const char *name, const char*argument);
|
||||
|
|
|
|||
|
|
@ -179,7 +179,7 @@ void pa_namereg_unregister(pa_core *c, const char *name) {
|
|||
pa_xfree(e);
|
||||
}
|
||||
|
||||
void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, int autoload) {
|
||||
void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, pa_bool_t autoload) {
|
||||
struct namereg_entry *e;
|
||||
uint32_t idx;
|
||||
pa_assert(c);
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ void pa_namereg_free(pa_core *c);
|
|||
|
||||
const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t type, void *data, int fail);
|
||||
void pa_namereg_unregister(pa_core *c, const char *name);
|
||||
void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, int autoload);
|
||||
void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, pa_bool_t autoload);
|
||||
int pa_namereg_set_default(pa_core*c, const char *name, pa_namereg_type_t type);
|
||||
|
||||
const char *pa_namereg_get_default_sink_name(pa_core *c);
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@ enum {
|
|||
PA_COMMAND_SUSPEND_SINK,
|
||||
PA_COMMAND_SUSPEND_SOURCE,
|
||||
|
||||
/* Supported since protocol v13 (0.9.8) */
|
||||
/* Supported since protocol v12 (0.9.8) */
|
||||
PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR,
|
||||
PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR,
|
||||
|
||||
|
|
@ -139,6 +139,17 @@ enum {
|
|||
PA_COMMAND_PLAYBACK_STREAM_MOVED,
|
||||
PA_COMMAND_RECORD_STREAM_MOVED,
|
||||
|
||||
/* Supported since protocol v13 (0.9.10) */
|
||||
PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST,
|
||||
PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST,
|
||||
PA_COMMAND_UPDATE_CLIENT_PROPLIST,
|
||||
PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST,
|
||||
PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST,
|
||||
PA_COMMAND_REMOVE_CLIENT_PROPLIST,
|
||||
|
||||
/* SERVER->CLIENT */
|
||||
PA_COMMAND_STARTED,
|
||||
|
||||
PA_COMMAND_MAX
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -144,16 +144,16 @@ fail:
|
|||
int pa_pid_file_create(void) {
|
||||
int fd = -1;
|
||||
int ret = -1;
|
||||
char fn[PATH_MAX];
|
||||
char t[20];
|
||||
pid_t pid;
|
||||
size_t l;
|
||||
char *fn;
|
||||
|
||||
#ifdef OS_IS_WIN32
|
||||
HANDLE process;
|
||||
#endif
|
||||
|
||||
pa_runtime_path("pid", fn, sizeof(fn));
|
||||
fn = pa_runtime_path("pid");
|
||||
|
||||
if ((fd = open_pid_file(fn, O_CREAT|O_RDWR)) < 0)
|
||||
goto fail;
|
||||
|
|
@ -200,17 +200,19 @@ fail:
|
|||
}
|
||||
}
|
||||
|
||||
pa_xfree(fn);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Remove the PID file, if it is ours */
|
||||
int pa_pid_file_remove(void) {
|
||||
int fd = -1;
|
||||
char fn[PATH_MAX];
|
||||
char *fn;
|
||||
int ret = -1;
|
||||
pid_t pid;
|
||||
|
||||
pa_runtime_path("pid", fn, sizeof(fn));
|
||||
fn = pa_runtime_path("pid");
|
||||
|
||||
if ((fd = open_pid_file(fn, O_RDWR)) < 0) {
|
||||
pa_log_warn("Failed to open PID file '%s': %s", fn, pa_cstrerror(errno));
|
||||
|
|
@ -254,6 +256,8 @@ fail:
|
|||
}
|
||||
}
|
||||
|
||||
pa_xfree(fn);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -272,7 +276,7 @@ int pa_pid_file_check_running(pid_t *pid, const char *binary_name) {
|
|||
* process. */
|
||||
int pa_pid_file_kill(int sig, pid_t *pid, const char *binary_name) {
|
||||
int fd = -1;
|
||||
char fn[PATH_MAX];
|
||||
char *fn;
|
||||
int ret = -1;
|
||||
pid_t _pid;
|
||||
#ifdef __linux__
|
||||
|
|
@ -281,7 +285,7 @@ int pa_pid_file_kill(int sig, pid_t *pid, const char *binary_name) {
|
|||
if (!pid)
|
||||
pid = &_pid;
|
||||
|
||||
pa_runtime_path("pid", fn, sizeof(fn));
|
||||
fn = pa_runtime_path("pid");
|
||||
|
||||
if ((fd = open_pid_file(fn, O_RDONLY)) < 0)
|
||||
goto fail;
|
||||
|
|
@ -296,7 +300,7 @@ int pa_pid_file_kill(int sig, pid_t *pid, const char *binary_name) {
|
|||
if ((e = pa_readlink(fn))) {
|
||||
char *f = pa_path_get_filename(e);
|
||||
if (strcmp(f, binary_name)
|
||||
#if defined(__OPTIMIZE__)
|
||||
#if !defined(__OPTIMIZE__)
|
||||
/* libtool likes to rename our binary names ... */
|
||||
&& !(pa_startswith(f, "lt-") && strcmp(f+3, binary_name) == 0)
|
||||
#endif
|
||||
|
|
@ -319,6 +323,8 @@ fail:
|
|||
pa_xfree(e);
|
||||
#endif
|
||||
|
||||
pa_xfree(fn);
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
/***
|
||||
This file is part of PulseAudio.
|
||||
|
||||
Copyright 2006 Lennart Poettering
|
||||
Copyright 2006-2008 Lennart Poettering
|
||||
|
||||
PulseAudio is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published
|
||||
|
|
@ -30,10 +30,11 @@
|
|||
#include <string.h>
|
||||
|
||||
#include <pulse/xmalloc.h>
|
||||
#include <pulse/gccmacro.h>
|
||||
|
||||
#include <pulsecore/sink-input.h>
|
||||
#include <pulsecore/gccmacro.h>
|
||||
#include <pulsecore/thread-mq.h>
|
||||
#include <pulsecore/sample-util.h>
|
||||
|
||||
#include "play-memblockq.h"
|
||||
|
||||
|
|
@ -59,7 +60,6 @@ static void memblockq_stream_unlink(memblockq_stream *u) {
|
|||
return;
|
||||
|
||||
pa_sink_input_unlink(u->sink_input);
|
||||
|
||||
pa_sink_input_unref(u->sink_input);
|
||||
u->sink_input = NULL;
|
||||
|
||||
|
|
@ -70,8 +70,6 @@ static void memblockq_stream_free(pa_object *o) {
|
|||
memblockq_stream *u = MEMBLOCKQ_STREAM(o);
|
||||
pa_assert(u);
|
||||
|
||||
memblockq_stream_unlink(u);
|
||||
|
||||
if (u->memblockq)
|
||||
pa_memblockq_free(u->memblockq);
|
||||
|
||||
|
|
@ -92,15 +90,34 @@ static int memblockq_stream_process_msg(pa_msgobject *o, int code, void*userdata
|
|||
}
|
||||
|
||||
static void sink_input_kill_cb(pa_sink_input *i) {
|
||||
pa_sink_input_assert_ref(i);
|
||||
|
||||
memblockq_stream_unlink(MEMBLOCKQ_STREAM(i->userdata));
|
||||
}
|
||||
|
||||
static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
|
||||
memblockq_stream *u;
|
||||
|
||||
pa_assert(i);
|
||||
pa_sink_input_assert_ref(i);
|
||||
u = MEMBLOCKQ_STREAM(i->userdata);
|
||||
memblockq_stream_assert_ref(u);
|
||||
|
||||
memblockq_stream_unlink(u);
|
||||
}
|
||||
|
||||
/* Called from IO thread context */
|
||||
static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) {
|
||||
memblockq_stream *u;
|
||||
|
||||
pa_sink_input_assert_ref(i);
|
||||
u = MEMBLOCKQ_STREAM(i->userdata);
|
||||
memblockq_stream_assert_ref(u);
|
||||
|
||||
/* If we are added for the first time, ask for a rewinding so that
|
||||
* we are heard right-away. */
|
||||
if (PA_SINK_INPUT_IS_LINKED(state) &&
|
||||
i->thread_info.state == PA_SINK_INPUT_INIT)
|
||||
pa_sink_input_request_rewind(i, 0, FALSE, TRUE);
|
||||
}
|
||||
|
||||
static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
|
||||
memblockq_stream *u;
|
||||
|
||||
pa_sink_input_assert_ref(i);
|
||||
pa_assert(chunk);
|
||||
u = MEMBLOCKQ_STREAM(i->userdata);
|
||||
memblockq_stream_assert_ref(u);
|
||||
|
|
@ -109,36 +126,57 @@ static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chun
|
|||
return -1;
|
||||
|
||||
if (pa_memblockq_peek(u->memblockq, chunk) < 0) {
|
||||
pa_memblockq_free(u->memblockq);
|
||||
u->memblockq = NULL;
|
||||
pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u), MEMBLOCKQ_STREAM_MESSAGE_UNLINK, NULL, 0, NULL, NULL);
|
||||
|
||||
if (pa_sink_input_safe_to_remove(i)) {
|
||||
|
||||
pa_memblockq_free(u->memblockq);
|
||||
u->memblockq = NULL;
|
||||
|
||||
pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u), MEMBLOCKQ_STREAM_MESSAGE_UNLINK, NULL, 0, NULL, NULL);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
pa_memblockq_drop(u->memblockq, chunk->length);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sink_input_drop_cb(pa_sink_input *i, size_t length) {
|
||||
static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
|
||||
memblockq_stream *u;
|
||||
|
||||
pa_assert(i);
|
||||
pa_assert(length > 0);
|
||||
pa_sink_input_assert_ref(i);
|
||||
pa_assert(nbytes > 0);
|
||||
u = MEMBLOCKQ_STREAM(i->userdata);
|
||||
memblockq_stream_assert_ref(u);
|
||||
|
||||
if (!u->memblockq)
|
||||
return;
|
||||
|
||||
pa_memblockq_drop(u->memblockq, length);
|
||||
pa_memblockq_rewind(u->memblockq, nbytes);
|
||||
}
|
||||
|
||||
static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
|
||||
memblockq_stream *u;
|
||||
|
||||
pa_sink_input_assert_ref(i);
|
||||
u = MEMBLOCKQ_STREAM(i->userdata);
|
||||
memblockq_stream_assert_ref(u);
|
||||
|
||||
if (!u->memblockq)
|
||||
return;
|
||||
|
||||
pa_memblockq_set_maxrewind(u->memblockq, nbytes);
|
||||
}
|
||||
|
||||
pa_sink_input* pa_memblockq_sink_input_new(
|
||||
pa_sink *sink,
|
||||
const char *name,
|
||||
const pa_sample_spec *ss,
|
||||
const pa_channel_map *map,
|
||||
pa_memblockq *q,
|
||||
pa_cvolume *volume) {
|
||||
pa_cvolume *volume,
|
||||
pa_proplist *p) {
|
||||
|
||||
memblockq_stream *u = NULL;
|
||||
pa_sink_input_new_data data;
|
||||
|
|
@ -149,41 +187,36 @@ pa_sink_input* pa_memblockq_sink_input_new(
|
|||
/* We allow creating this stream with no q set, so that it can be
|
||||
* filled in later */
|
||||
|
||||
if (q && pa_memblockq_get_length(q) <= 0) {
|
||||
pa_memblockq_free(q);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (volume && pa_cvolume_is_muted(volume)) {
|
||||
pa_memblockq_free(q);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
u = pa_msgobject_new(memblockq_stream);
|
||||
u->parent.parent.free = memblockq_stream_free;
|
||||
u->parent.process_msg = memblockq_stream_process_msg;
|
||||
u->core = sink->core;
|
||||
u->sink_input = NULL;
|
||||
u->memblockq = q;
|
||||
u->memblockq = NULL;
|
||||
|
||||
pa_sink_input_new_data_init(&data);
|
||||
data.sink = sink;
|
||||
data.name = name;
|
||||
data.driver = __FILE__;
|
||||
pa_sink_input_new_data_set_sample_spec(&data, ss);
|
||||
pa_sink_input_new_data_set_channel_map(&data, map);
|
||||
pa_sink_input_new_data_set_volume(&data, volume);
|
||||
pa_proplist_update(data.proplist, PA_UPDATE_REPLACE, p);
|
||||
|
||||
if (!(u->sink_input = pa_sink_input_new(sink->core, &data, 0)))
|
||||
u->sink_input = pa_sink_input_new(sink->core, &data, 0);
|
||||
pa_sink_input_new_data_done(&data);
|
||||
|
||||
if (!u->sink_input)
|
||||
goto fail;
|
||||
|
||||
u->sink_input->peek = sink_input_peek_cb;
|
||||
u->sink_input->drop = sink_input_drop_cb;
|
||||
u->sink_input->pop = sink_input_pop_cb;
|
||||
u->sink_input->process_rewind = sink_input_process_rewind_cb;
|
||||
u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
|
||||
u->sink_input->kill = sink_input_kill_cb;
|
||||
u->sink_input->state_change = sink_input_state_change_cb;
|
||||
u->sink_input->userdata = u;
|
||||
|
||||
if (q)
|
||||
pa_memblockq_prebuf_disable(q);
|
||||
pa_memblockq_sink_input_set_queue(u->sink_input, q);
|
||||
|
||||
/* The reference to u is dangling here, because we want
|
||||
* to keep this stream around until it is fully played. */
|
||||
|
|
@ -202,11 +235,12 @@ fail:
|
|||
|
||||
int pa_play_memblockq(
|
||||
pa_sink *sink,
|
||||
const char *name,
|
||||
const pa_sample_spec *ss,
|
||||
const pa_channel_map *map,
|
||||
pa_memblockq *q,
|
||||
pa_cvolume *volume) {
|
||||
pa_cvolume *volume,
|
||||
pa_proplist *p,
|
||||
uint32_t *sink_input_index) {
|
||||
|
||||
pa_sink_input *i;
|
||||
|
||||
|
|
@ -214,10 +248,14 @@ int pa_play_memblockq(
|
|||
pa_assert(ss);
|
||||
pa_assert(q);
|
||||
|
||||
if (!(i = pa_memblockq_sink_input_new(sink, name, ss, map, q, volume)))
|
||||
if (!(i = pa_memblockq_sink_input_new(sink, ss, map, q, volume, p)))
|
||||
return -1;
|
||||
|
||||
pa_sink_input_put(i);
|
||||
|
||||
if (sink_input_index)
|
||||
*sink_input_index = i->index;
|
||||
|
||||
pa_sink_input_unref(i);
|
||||
|
||||
return 0;
|
||||
|
|
@ -232,5 +270,10 @@ void pa_memblockq_sink_input_set_queue(pa_sink_input *i, pa_memblockq *q) {
|
|||
|
||||
if (u->memblockq)
|
||||
pa_memblockq_free(u->memblockq);
|
||||
u->memblockq = q;
|
||||
|
||||
if ((u->memblockq = q)) {
|
||||
pa_memblockq_set_prebuf(q, 0);
|
||||
pa_memblockq_set_silence(q, NULL);
|
||||
pa_memblockq_willneed(q);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,20 +29,21 @@
|
|||
|
||||
pa_sink_input* pa_memblockq_sink_input_new(
|
||||
pa_sink *sink,
|
||||
const char *name,
|
||||
const pa_sample_spec *ss,
|
||||
const pa_channel_map *map,
|
||||
pa_memblockq *q,
|
||||
pa_cvolume *volume);
|
||||
pa_cvolume *volume,
|
||||
pa_proplist *p);
|
||||
|
||||
void pa_memblockq_sink_input_set_queue(pa_sink_input *i, pa_memblockq *q);
|
||||
|
||||
int pa_play_memblockq(
|
||||
pa_sink *sink,
|
||||
const char *name,
|
||||
const pa_sample_spec *ss,
|
||||
const pa_channel_map *map,
|
||||
pa_memblockq *q,
|
||||
pa_cvolume *cvolume);
|
||||
pa_cvolume *cvolume,
|
||||
pa_proplist *p,
|
||||
uint32_t *sink_input_index);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
/***
|
||||
This file is part of PulseAudio.
|
||||
|
||||
Copyright 2004-2006 Lennart Poettering
|
||||
Copyright 2004-2008 Lennart Poettering
|
||||
|
||||
PulseAudio is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published
|
||||
|
|
@ -30,167 +30,37 @@
|
|||
#include <string.h>
|
||||
|
||||
#include <pulse/xmalloc.h>
|
||||
#include <pulse/gccmacro.h>
|
||||
|
||||
#include <pulsecore/sink-input.h>
|
||||
#include <pulsecore/gccmacro.h>
|
||||
#include <pulsecore/thread-mq.h>
|
||||
#include <pulsecore/play-memblockq.h>
|
||||
|
||||
#include "play-memchunk.h"
|
||||
|
||||
typedef struct memchunk_stream {
|
||||
pa_msgobject parent;
|
||||
pa_core *core;
|
||||
pa_sink_input *sink_input;
|
||||
pa_memchunk memchunk;
|
||||
} memchunk_stream;
|
||||
|
||||
enum {
|
||||
MEMCHUNK_STREAM_MESSAGE_UNLINK,
|
||||
};
|
||||
|
||||
PA_DECLARE_CLASS(memchunk_stream);
|
||||
#define MEMCHUNK_STREAM(o) (memchunk_stream_cast(o))
|
||||
static PA_DEFINE_CHECK_TYPE(memchunk_stream, pa_msgobject);
|
||||
|
||||
static void memchunk_stream_unlink(memchunk_stream *u) {
|
||||
pa_assert(u);
|
||||
|
||||
if (!u->sink_input)
|
||||
return;
|
||||
|
||||
pa_sink_input_unlink(u->sink_input);
|
||||
|
||||
pa_sink_input_unref(u->sink_input);
|
||||
u->sink_input = NULL;
|
||||
|
||||
memchunk_stream_unref(u);
|
||||
}
|
||||
|
||||
static void memchunk_stream_free(pa_object *o) {
|
||||
memchunk_stream *u = MEMCHUNK_STREAM(o);
|
||||
pa_assert(u);
|
||||
|
||||
memchunk_stream_unlink(u);
|
||||
|
||||
if (u->memchunk.memblock)
|
||||
pa_memblock_unref(u->memchunk.memblock);
|
||||
|
||||
pa_xfree(u);
|
||||
}
|
||||
|
||||
static int memchunk_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
|
||||
memchunk_stream *u = MEMCHUNK_STREAM(o);
|
||||
memchunk_stream_assert_ref(u);
|
||||
|
||||
switch (code) {
|
||||
case MEMCHUNK_STREAM_MESSAGE_UNLINK:
|
||||
memchunk_stream_unlink(u);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sink_input_kill_cb(pa_sink_input *i) {
|
||||
pa_sink_input_assert_ref(i);
|
||||
|
||||
memchunk_stream_unlink(MEMCHUNK_STREAM(i->userdata));
|
||||
}
|
||||
|
||||
static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
|
||||
memchunk_stream *u;
|
||||
|
||||
pa_assert(i);
|
||||
pa_assert(chunk);
|
||||
u = MEMCHUNK_STREAM(i->userdata);
|
||||
memchunk_stream_assert_ref(u);
|
||||
|
||||
if (!u->memchunk.memblock)
|
||||
return -1;
|
||||
|
||||
if (u->memchunk.length <= 0) {
|
||||
pa_memblock_unref(u->memchunk.memblock);
|
||||
u->memchunk.memblock = NULL;
|
||||
pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u), MEMCHUNK_STREAM_MESSAGE_UNLINK, NULL, 0, NULL, NULL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pa_assert(u->memchunk.memblock);
|
||||
*chunk = u->memchunk;
|
||||
pa_memblock_ref(chunk->memblock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sink_input_drop_cb(pa_sink_input *i, size_t length) {
|
||||
memchunk_stream *u;
|
||||
|
||||
pa_assert(i);
|
||||
pa_assert(length > 0);
|
||||
u = MEMCHUNK_STREAM(i->userdata);
|
||||
memchunk_stream_assert_ref(u);
|
||||
|
||||
if (length < u->memchunk.length) {
|
||||
u->memchunk.length -= length;
|
||||
u->memchunk.index += length;
|
||||
} else
|
||||
u->memchunk.length = 0;
|
||||
}
|
||||
|
||||
int pa_play_memchunk(
|
||||
pa_sink *sink,
|
||||
const char *name,
|
||||
const pa_sample_spec *ss,
|
||||
const pa_channel_map *map,
|
||||
const pa_memchunk *chunk,
|
||||
pa_cvolume *volume) {
|
||||
pa_cvolume *volume,
|
||||
pa_proplist *p,
|
||||
uint32_t *sink_input_index) {
|
||||
|
||||
memchunk_stream *u = NULL;
|
||||
pa_sink_input_new_data data;
|
||||
pa_memblockq *q;
|
||||
int r;
|
||||
|
||||
pa_assert(sink);
|
||||
pa_assert(ss);
|
||||
pa_assert(chunk);
|
||||
|
||||
if (volume && pa_cvolume_is_muted(volume))
|
||||
return 0;
|
||||
q = pa_memblockq_new(0, chunk->length, 0, pa_frame_size(ss), 1, 1, 0, NULL);
|
||||
pa_assert_se(pa_memblockq_push(q, chunk) >= 0);
|
||||
|
||||
pa_memchunk_will_need(chunk);
|
||||
|
||||
u = pa_msgobject_new(memchunk_stream);
|
||||
u->parent.parent.free = memchunk_stream_free;
|
||||
u->parent.process_msg = memchunk_stream_process_msg;
|
||||
u->core = sink->core;
|
||||
u->memchunk = *chunk;
|
||||
pa_memblock_ref(u->memchunk.memblock);
|
||||
|
||||
pa_sink_input_new_data_init(&data);
|
||||
data.sink = sink;
|
||||
data.driver = __FILE__;
|
||||
data.name = name;
|
||||
pa_sink_input_new_data_set_sample_spec(&data, ss);
|
||||
pa_sink_input_new_data_set_channel_map(&data, map);
|
||||
pa_sink_input_new_data_set_volume(&data, volume);
|
||||
|
||||
if (!(u->sink_input = pa_sink_input_new(sink->core, &data, 0)))
|
||||
goto fail;
|
||||
|
||||
u->sink_input->peek = sink_input_peek_cb;
|
||||
u->sink_input->drop = sink_input_drop_cb;
|
||||
u->sink_input->kill = sink_input_kill_cb;
|
||||
u->sink_input->userdata = u;
|
||||
|
||||
pa_sink_input_put(u->sink_input);
|
||||
|
||||
/* The reference to u is dangling here, because we want to keep
|
||||
* this stream around until it is fully played. */
|
||||
if ((r = pa_play_memblockq(sink, ss, map, q, volume, p, sink_input_index)) < 0) {
|
||||
pa_memblockq_free(q);
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
if (u)
|
||||
memchunk_stream_unref(u);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,10 +29,11 @@
|
|||
|
||||
int pa_play_memchunk(
|
||||
pa_sink *sink,
|
||||
const char *name,
|
||||
const pa_sample_spec *ss,
|
||||
const pa_channel_map *map,
|
||||
const pa_memchunk *chunk,
|
||||
pa_cvolume *cvolume);
|
||||
pa_cvolume *cvolume,
|
||||
pa_proplist *p,
|
||||
uint32_t *sink_input_index);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ pa_protocol_cli* pa_protocol_cli_new(pa_core *core, pa_socket_server *server, pa
|
|||
p = pa_xnew(pa_protocol_cli, 1);
|
||||
p->module = m;
|
||||
p->core = core;
|
||||
p->server = server;
|
||||
p->server = pa_socket_server_ref(server);
|
||||
p->connections = pa_idxset_new(NULL, NULL);
|
||||
|
||||
pa_socket_server_set_callback(p->server, on_connection, p);
|
||||
|
|
|
|||
|
|
@ -70,10 +70,12 @@
|
|||
#define PLAYBACK_BUFFER_SECONDS (.25)
|
||||
#define PLAYBACK_BUFFER_FRAGMENTS (10)
|
||||
#define RECORD_BUFFER_SECONDS (5)
|
||||
#define RECORD_BUFFER_FRAGMENTS (100)
|
||||
|
||||
#define MAX_CACHE_SAMPLE_SIZE (2048000)
|
||||
|
||||
#define DEFAULT_SINK_LATENCY (150*PA_USEC_PER_MSEC)
|
||||
#define DEFAULT_SOURCE_LATENCY (150*PA_USEC_PER_MSEC)
|
||||
|
||||
#define SCACHE_PREFIX "esound."
|
||||
|
||||
/* This is heavily based on esound's code */
|
||||
|
|
@ -102,8 +104,9 @@ typedef struct connection {
|
|||
|
||||
struct {
|
||||
pa_memblock *current_memblock;
|
||||
size_t memblock_index, fragment_size;
|
||||
size_t memblock_index;
|
||||
pa_atomic_t missing;
|
||||
pa_bool_t underrun;
|
||||
} playback;
|
||||
|
||||
struct {
|
||||
|
|
@ -122,7 +125,7 @@ static PA_DEFINE_CHECK_TYPE(connection, pa_msgobject);
|
|||
struct pa_protocol_esound {
|
||||
pa_module *module;
|
||||
pa_core *core;
|
||||
int public;
|
||||
pa_bool_t public;
|
||||
pa_socket_server *server;
|
||||
pa_idxset *connections;
|
||||
|
||||
|
|
@ -149,8 +152,9 @@ typedef struct proto_handler {
|
|||
const char *description;
|
||||
} esd_proto_handler_info_t;
|
||||
|
||||
static void sink_input_drop_cb(pa_sink_input *i, size_t length);
|
||||
static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk);
|
||||
static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk);
|
||||
static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes);
|
||||
static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes);
|
||||
static void sink_input_kill_cb(pa_sink_input *i);
|
||||
static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
|
||||
static pa_usec_t source_output_get_latency_cb(pa_source_output *o);
|
||||
|
|
@ -398,8 +402,7 @@ static int esd_proto_stream_play(connection *c, PA_GCC_UNUSED esd_proto_t reques
|
|||
CHECK_VALIDITY(sink, "No such sink: %s", c->protocol->sink_name);
|
||||
}
|
||||
|
||||
strncpy(name, data, sizeof(name));
|
||||
name[sizeof(name)-1] = 0;
|
||||
pa_strlcpy(name, data, sizeof(name));
|
||||
|
||||
utf8_name = pa_utf8_filter(name);
|
||||
pa_client_set_name(c->client, utf8_name);
|
||||
|
|
@ -410,34 +413,39 @@ static int esd_proto_stream_play(connection *c, PA_GCC_UNUSED esd_proto_t reques
|
|||
pa_assert(!c->sink_input && !c->input_memblockq);
|
||||
|
||||
pa_sink_input_new_data_init(&sdata);
|
||||
sdata.sink = sink;
|
||||
sdata.driver = __FILE__;
|
||||
sdata.name = c->client->name;
|
||||
pa_sink_input_new_data_set_sample_spec(&sdata, &ss);
|
||||
sdata.module = c->protocol->module;
|
||||
sdata.client = c->client;
|
||||
sdata.sink = sink;
|
||||
pa_proplist_update(sdata.proplist, PA_UPDATE_MERGE, c->client->proplist);
|
||||
pa_sink_input_new_data_set_sample_spec(&sdata, &ss);
|
||||
|
||||
c->sink_input = pa_sink_input_new(c->protocol->core, &sdata, 0);
|
||||
pa_sink_input_new_data_done(&sdata);
|
||||
|
||||
CHECK_VALIDITY(c->sink_input, "Failed to create sink input.");
|
||||
|
||||
l = (size_t) (pa_bytes_per_second(&ss)*PLAYBACK_BUFFER_SECONDS);
|
||||
c->input_memblockq = pa_memblockq_new(
|
||||
0,
|
||||
l,
|
||||
0,
|
||||
l,
|
||||
pa_frame_size(&ss),
|
||||
(size_t) -1,
|
||||
l/PLAYBACK_BUFFER_FRAGMENTS,
|
||||
0,
|
||||
NULL);
|
||||
pa_iochannel_socket_set_rcvbuf(c->io, l/PLAYBACK_BUFFER_FRAGMENTS*2);
|
||||
c->playback.fragment_size = l/PLAYBACK_BUFFER_FRAGMENTS;
|
||||
pa_iochannel_socket_set_rcvbuf(c->io, l);
|
||||
|
||||
c->sink_input->parent.process_msg = sink_input_process_msg;
|
||||
c->sink_input->peek = sink_input_peek_cb;
|
||||
c->sink_input->drop = sink_input_drop_cb;
|
||||
c->sink_input->pop = sink_input_pop_cb;
|
||||
c->sink_input->process_rewind = sink_input_process_rewind_cb;
|
||||
c->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
|
||||
c->sink_input->kill = sink_input_kill_cb;
|
||||
c->sink_input->userdata = c;
|
||||
|
||||
pa_sink_input_set_requested_latency(c->sink_input, DEFAULT_SINK_LATENCY);
|
||||
|
||||
c->state = ESD_STREAMING_DATA;
|
||||
|
||||
c->protocol->n_player++;
|
||||
|
|
@ -497,8 +505,7 @@ static int esd_proto_stream_record(connection *c, esd_proto_t request, const voi
|
|||
}
|
||||
}
|
||||
|
||||
strncpy(name, data, sizeof(name));
|
||||
name[sizeof(name)-1] = 0;
|
||||
pa_strlcpy(name, data, sizeof(name));
|
||||
|
||||
utf8_name = pa_utf8_filter(name);
|
||||
pa_client_set_name(c->client, utf8_name);
|
||||
|
|
@ -509,32 +516,37 @@ static int esd_proto_stream_record(connection *c, esd_proto_t request, const voi
|
|||
pa_assert(!c->output_memblockq && !c->source_output);
|
||||
|
||||
pa_source_output_new_data_init(&sdata);
|
||||
sdata.source = source;
|
||||
sdata.driver = __FILE__;
|
||||
sdata.name = c->client->name;
|
||||
pa_source_output_new_data_set_sample_spec(&sdata, &ss);
|
||||
sdata.module = c->protocol->module;
|
||||
sdata.client = c->client;
|
||||
sdata.source = source;
|
||||
pa_proplist_update(sdata.proplist, PA_UPDATE_MERGE, c->client->proplist);
|
||||
pa_source_output_new_data_set_sample_spec(&sdata, &ss);
|
||||
|
||||
c->source_output = pa_source_output_new(c->protocol->core, &sdata, 9);
|
||||
CHECK_VALIDITY(c->source_output, "Failed to create source_output.");
|
||||
c->source_output = pa_source_output_new(c->protocol->core, &sdata, 0);
|
||||
pa_source_output_new_data_done(&sdata);
|
||||
|
||||
CHECK_VALIDITY(c->source_output, "Failed to create source output.");
|
||||
|
||||
l = (size_t) (pa_bytes_per_second(&ss)*RECORD_BUFFER_SECONDS);
|
||||
c->output_memblockq = pa_memblockq_new(
|
||||
0,
|
||||
l,
|
||||
0,
|
||||
l,
|
||||
pa_frame_size(&ss),
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
NULL);
|
||||
pa_iochannel_socket_set_sndbuf(c->io, l/RECORD_BUFFER_FRAGMENTS*2);
|
||||
pa_iochannel_socket_set_sndbuf(c->io, l);
|
||||
|
||||
c->source_output->push = source_output_push_cb;
|
||||
c->source_output->kill = source_output_kill_cb;
|
||||
c->source_output->get_latency = source_output_get_latency_cb;
|
||||
c->source_output->userdata = c;
|
||||
|
||||
pa_source_output_set_requested_latency(c->source_output, DEFAULT_SOURCE_LATENCY);
|
||||
|
||||
c->state = ESD_STREAMING_DATA;
|
||||
|
||||
c->protocol->n_player++;
|
||||
|
|
@ -638,8 +650,8 @@ static int esd_proto_all_info(connection *c, esd_proto_t request, const void *da
|
|||
memset(name, 0, ESD_NAME_MAX); /* don't leak old data */
|
||||
if (conn->original_name)
|
||||
strncpy(name, conn->original_name, ESD_NAME_MAX);
|
||||
else if (conn->client && conn->client->name)
|
||||
strncpy(name, conn->client->name, ESD_NAME_MAX);
|
||||
else if (conn->client && pa_proplist_gets(conn->client->proplist, PA_PROP_APPLICATION_NAME))
|
||||
strncpy(name, pa_proplist_gets(conn->client->proplist, PA_PROP_APPLICATION_NAME), ESD_NAME_MAX);
|
||||
connection_write(c, name, ESD_NAME_MAX);
|
||||
|
||||
/* rate */
|
||||
|
|
@ -785,8 +797,7 @@ static int esd_proto_sample_cache(connection *c, PA_GCC_UNUSED esd_proto_t reque
|
|||
CHECK_VALIDITY(sc_length <= MAX_CACHE_SAMPLE_SIZE, "Sample too large (%d bytes).", (int)sc_length);
|
||||
|
||||
strcpy(name, SCACHE_PREFIX);
|
||||
strncpy(name+sizeof(SCACHE_PREFIX)-1, data, ESD_NAME_MAX);
|
||||
name[sizeof(name)-1] = 0;
|
||||
pa_strlcpy(name+sizeof(SCACHE_PREFIX)-1, data, ESD_NAME_MAX);
|
||||
|
||||
CHECK_VALIDITY(pa_utf8_valid(name), "Invalid UTF8 in sample name.");
|
||||
|
||||
|
|
@ -800,7 +811,7 @@ static int esd_proto_sample_cache(connection *c, PA_GCC_UNUSED esd_proto_t reque
|
|||
|
||||
c->state = ESD_CACHING_SAMPLE;
|
||||
|
||||
pa_scache_add_item(c->protocol->core, c->scache.name, NULL, NULL, NULL, &idx);
|
||||
pa_scache_add_item(c->protocol->core, c->scache.name, NULL, NULL, NULL, c->client->proplist, &idx);
|
||||
|
||||
idx += 1;
|
||||
connection_write(c, &idx, sizeof(uint32_t));
|
||||
|
|
@ -818,8 +829,7 @@ static int esd_proto_sample_get_id(connection *c, PA_GCC_UNUSED esd_proto_t requ
|
|||
pa_assert(length == ESD_NAME_MAX);
|
||||
|
||||
strcpy(name, SCACHE_PREFIX);
|
||||
strncpy(name+sizeof(SCACHE_PREFIX)-1, data, ESD_NAME_MAX);
|
||||
name[sizeof(name)-1] = 0;
|
||||
pa_strlcpy(name+sizeof(SCACHE_PREFIX)-1, data, ESD_NAME_MAX);
|
||||
|
||||
CHECK_VALIDITY(pa_utf8_valid(name), "Invalid UTF8 in sample name.");
|
||||
|
||||
|
|
@ -851,7 +861,7 @@ static int esd_proto_sample_free_or_play(connection *c, esd_proto_t request, con
|
|||
pa_sink *sink;
|
||||
|
||||
if ((sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1)))
|
||||
if (pa_scache_play_item(c->protocol->core, name, sink, PA_VOLUME_NORM) >= 0)
|
||||
if (pa_scache_play_item(c->protocol->core, name, sink, PA_VOLUME_NORM, c->client->proplist, NULL) >= 0)
|
||||
ok = idx + 1;
|
||||
} else {
|
||||
pa_assert(request == ESD_PROTO_SAMPLE_FREE);
|
||||
|
|
@ -992,7 +1002,7 @@ static int do_read(connection *c) {
|
|||
uint32_t idx;
|
||||
|
||||
c->scache.memchunk.index = 0;
|
||||
pa_scache_add_item(c->protocol->core, c->scache.name, &c->scache.sample_spec, NULL, &c->scache.memchunk, &idx);
|
||||
pa_scache_add_item(c->protocol->core, c->scache.name, &c->scache.sample_spec, NULL, &c->scache.memchunk, c->client->proplist, &idx);
|
||||
|
||||
pa_memblock_unref(c->scache.memchunk.memblock);
|
||||
c->scache.memchunk.memblock = NULL;
|
||||
|
|
@ -1012,6 +1022,7 @@ static int do_read(connection *c) {
|
|||
ssize_t r;
|
||||
size_t l;
|
||||
void *p;
|
||||
size_t space;
|
||||
|
||||
pa_assert(c->input_memblockq);
|
||||
|
||||
|
|
@ -1020,21 +1031,26 @@ static int do_read(connection *c) {
|
|||
if (!(l = pa_atomic_load(&c->playback.missing)))
|
||||
return 0;
|
||||
|
||||
if (l > c->playback.fragment_size)
|
||||
l = c->playback.fragment_size;
|
||||
if (c->playback.current_memblock) {
|
||||
|
||||
if (c->playback.current_memblock)
|
||||
if (pa_memblock_get_length(c->playback.current_memblock) - c->playback.memblock_index < l) {
|
||||
space = pa_memblock_get_length(c->playback.current_memblock) - c->playback.memblock_index;
|
||||
|
||||
if (space <= 0) {
|
||||
pa_memblock_unref(c->playback.current_memblock);
|
||||
c->playback.current_memblock = NULL;
|
||||
c->playback.memblock_index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!c->playback.current_memblock) {
|
||||
pa_assert_se(c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, c->playback.fragment_size*2));
|
||||
pa_assert_se(c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, 0));
|
||||
c->playback.memblock_index = 0;
|
||||
|
||||
space = pa_memblock_get_length(c->playback.current_memblock);
|
||||
}
|
||||
|
||||
if (l > space)
|
||||
l = space;
|
||||
|
||||
p = pa_memblock_acquire(c->playback.current_memblock);
|
||||
r = pa_iochannel_read(c->io, (uint8_t*) p+c->playback.memblock_index, l);
|
||||
pa_memblock_release(c->playback.current_memblock);
|
||||
|
|
@ -1122,12 +1138,11 @@ static void do_work(connection *c) {
|
|||
if (c->dead)
|
||||
return;
|
||||
|
||||
if (pa_iochannel_is_readable(c->io)) {
|
||||
if (pa_iochannel_is_readable(c->io))
|
||||
if (do_read(c) < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (c->state == ESD_STREAMING_DATA && c->source_output && pa_iochannel_is_hungup(c->io))
|
||||
if (c->state == ESD_STREAMING_DATA && !c->sink_input && pa_iochannel_is_hungup(c->io))
|
||||
/* In case we are in capture mode we will never call read()
|
||||
* on the socket, hence we need to detect the hangup manually
|
||||
* here, instead of simply waiting for read() to return 0. */
|
||||
|
|
@ -1212,15 +1227,19 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
|
|||
/* New data from the main loop */
|
||||
pa_memblockq_push_align(c->input_memblockq, chunk);
|
||||
|
||||
if (pa_memblockq_is_readable(c->input_memblockq) && c->playback.underrun) {
|
||||
pa_log_debug("Requesting rewind due to end of underrun.");
|
||||
pa_sink_input_request_rewind(c->sink_input, 0, FALSE, TRUE);
|
||||
}
|
||||
|
||||
/* pa_log("got data, %u", pa_memblockq_get_length(c->input_memblockq)); */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
case SINK_INPUT_MESSAGE_DISABLE_PREBUF: {
|
||||
case SINK_INPUT_MESSAGE_DISABLE_PREBUF:
|
||||
pa_memblockq_prebuf_disable(c->input_memblockq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
|
||||
pa_usec_t *r = userdata;
|
||||
|
|
@ -1237,41 +1256,62 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
|
|||
}
|
||||
|
||||
/* Called from thread context */
|
||||
static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
|
||||
static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
|
||||
connection*c;
|
||||
int r;
|
||||
|
||||
pa_assert(i);
|
||||
pa_sink_input_assert_ref(i);
|
||||
c = CONNECTION(i->userdata);
|
||||
connection_assert_ref(c);
|
||||
pa_assert(chunk);
|
||||
|
||||
if ((r = pa_memblockq_peek(c->input_memblockq, chunk)) < 0 && c->dead)
|
||||
pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_UNLINK_CONNECTION, NULL, 0, NULL, NULL);
|
||||
if (pa_memblockq_peek(c->input_memblockq, chunk) < 0) {
|
||||
|
||||
return r;
|
||||
c->playback.underrun = TRUE;
|
||||
|
||||
if (c->dead && pa_sink_input_safe_to_remove(i))
|
||||
pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_UNLINK_CONNECTION, NULL, 0, NULL, NULL);
|
||||
|
||||
return -1;
|
||||
} else {
|
||||
size_t m;
|
||||
|
||||
c->playback.underrun = FALSE;
|
||||
|
||||
pa_memblockq_drop(c->input_memblockq, chunk->length);
|
||||
m = pa_memblockq_pop_missing(c->input_memblockq);
|
||||
|
||||
if (m > 0)
|
||||
if (pa_atomic_add(&c->playback.missing, m) <= 0)
|
||||
pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Called from thread context */
|
||||
static void sink_input_drop_cb(pa_sink_input *i, size_t length) {
|
||||
connection*c;
|
||||
size_t old, new;
|
||||
static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
|
||||
connection *c;
|
||||
|
||||
pa_assert(i);
|
||||
pa_sink_input_assert_ref(i);
|
||||
c = CONNECTION(i->userdata);
|
||||
connection_assert_ref(c);
|
||||
pa_assert(length);
|
||||
|
||||
/* pa_log("DROP"); */
|
||||
/* If we are in an underrun, then we don't rewind */
|
||||
if (i->thread_info.underrun_for > 0)
|
||||
return;
|
||||
|
||||
old = pa_memblockq_missing(c->input_memblockq);
|
||||
pa_memblockq_drop(c->input_memblockq, length);
|
||||
new = pa_memblockq_missing(c->input_memblockq);
|
||||
pa_memblockq_rewind(c->input_memblockq, nbytes);
|
||||
}
|
||||
|
||||
if (new > old) {
|
||||
if (pa_atomic_add(&c->playback.missing, new - old) <= 0)
|
||||
pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL);
|
||||
}
|
||||
/* Called from thread context */
|
||||
static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
|
||||
connection *c;
|
||||
|
||||
pa_sink_input_assert_ref(i);
|
||||
c = CONNECTION(i->userdata);
|
||||
connection_assert_ref(c);
|
||||
|
||||
pa_memblockq_set_maxrewind(c->input_memblockq, nbytes);
|
||||
}
|
||||
|
||||
static void sink_input_kill_cb(pa_sink_input *i) {
|
||||
|
|
@ -1286,7 +1326,7 @@ static void sink_input_kill_cb(pa_sink_input *i) {
|
|||
static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
|
||||
connection *c;
|
||||
|
||||
pa_assert(o);
|
||||
pa_source_output_assert_ref(o);
|
||||
c = CONNECTION(o->userdata);
|
||||
pa_assert(c);
|
||||
pa_assert(chunk);
|
||||
|
|
@ -1303,7 +1343,7 @@ static void source_output_kill_cb(pa_source_output *o) {
|
|||
static pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
|
||||
connection*c;
|
||||
|
||||
pa_assert(o);
|
||||
pa_source_output_assert_ref(o);
|
||||
c = CONNECTION(o->userdata);
|
||||
pa_assert(c);
|
||||
|
||||
|
|
@ -1349,7 +1389,8 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
|
|||
pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname));
|
||||
pa_snprintf(cname, sizeof(cname), "EsounD client (%s)", pname);
|
||||
c->client = pa_client_new(p->core, __FILE__, cname);
|
||||
c->client->owner = p->module;
|
||||
pa_proplist_sets(c->client->proplist, "esound-protocol.peer", pname);
|
||||
c->client->module = p->module;
|
||||
c->client->kill = client_kill_cb;
|
||||
c->client->userdata = c;
|
||||
|
||||
|
|
@ -1374,11 +1415,10 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
|
|||
|
||||
c->playback.current_memblock = NULL;
|
||||
c->playback.memblock_index = 0;
|
||||
c->playback.fragment_size = 0;
|
||||
c->playback.underrun = TRUE;
|
||||
pa_atomic_store(&c->playback.missing, 0);
|
||||
|
||||
c->scache.memchunk.length = c->scache.memchunk.index = 0;
|
||||
c->scache.memchunk.memblock = NULL;
|
||||
pa_memchunk_reset(&c->scache.memchunk);
|
||||
c->scache.name = NULL;
|
||||
|
||||
c->original_name = NULL;
|
||||
|
|
@ -1436,7 +1476,7 @@ pa_protocol_esound* pa_protocol_esound_new(pa_core*core, pa_socket_server *serve
|
|||
p->core = core;
|
||||
p->module = m;
|
||||
p->public = public;
|
||||
p->server = server;
|
||||
p->server = pa_socket_server_ref(server);
|
||||
pa_socket_server_set_callback(p->server, on_connection, p);
|
||||
p->connections = pa_idxset_new(NULL, NULL);
|
||||
|
||||
|
|
@ -1459,7 +1499,8 @@ void pa_protocol_esound_free(pa_protocol_esound *p) {
|
|||
connection_unlink(c);
|
||||
pa_idxset_free(p->connections, NULL, NULL);
|
||||
|
||||
pa_socket_server_unref(p->server);
|
||||
if (p->server)
|
||||
pa_socket_server_unref(p->server);
|
||||
|
||||
if (p->auth_ip_acl)
|
||||
pa_ip_acl_free(p->auth_ip_acl);
|
||||
|
|
|
|||
|
|
@ -168,7 +168,7 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
|
|||
#define PRINTF_FIELD(a,b) pa_ioline_printf(c->line, "<tr><td><b>%s</b></td><td>%s</td></tr>\n",(a),(b))
|
||||
|
||||
PRINTF_FIELD("User Name:", pa_get_user_name(txt, sizeof(txt)));
|
||||
PRINTF_FIELD("Fully Qualified Domain Name:", pa_get_fqdn(txt, sizeof(txt)));
|
||||
PRINTF_FIELD("Host name:", pa_get_host_name(txt, sizeof(txt)));
|
||||
PRINTF_FIELD("Default Sample Specification:", pa_sample_spec_snprint(txt, sizeof(txt), &c->protocol->core->default_sample_spec));
|
||||
PRINTF_FIELD("Default Sink:", pa_namereg_get_default_sink_name(c->protocol->core));
|
||||
PRINTF_FIELD("Default Source:", pa_namereg_get_default_source_name(c->protocol->core));
|
||||
|
|
@ -255,7 +255,7 @@ pa_protocol_http* pa_protocol_http_new(pa_core *core, pa_socket_server *server,
|
|||
p = pa_xnew(pa_protocol_http, 1);
|
||||
p->module = m;
|
||||
p->core = core;
|
||||
p->server = server;
|
||||
p->server = pa_socket_server_ref(server);
|
||||
p->connections = pa_idxset_new(NULL, NULL);
|
||||
|
||||
pa_socket_server_set_callback(p->server, on_connection, p);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -32,6 +32,7 @@
|
|||
#include <string.h>
|
||||
|
||||
#include <pulse/xmalloc.h>
|
||||
#include <pulse/timeval.h>
|
||||
|
||||
#include <pulsecore/sink-input.h>
|
||||
#include <pulsecore/source-output.h>
|
||||
|
|
@ -42,6 +43,7 @@
|
|||
#include <pulsecore/core-error.h>
|
||||
#include <pulsecore/atomic.h>
|
||||
#include <pulsecore/thread-mq.h>
|
||||
#include <pulsecore/core-util.h>
|
||||
|
||||
#include "protocol-simple.h"
|
||||
|
||||
|
|
@ -57,12 +59,13 @@ typedef struct connection {
|
|||
pa_client *client;
|
||||
pa_memblockq *input_memblockq, *output_memblockq;
|
||||
|
||||
int dead;
|
||||
pa_bool_t dead;
|
||||
|
||||
struct {
|
||||
pa_memblock *current_memblock;
|
||||
size_t memblock_index, fragment_size;
|
||||
size_t memblock_index;
|
||||
pa_atomic_t missing;
|
||||
pa_bool_t underrun;
|
||||
} playback;
|
||||
} connection;
|
||||
|
||||
|
|
@ -101,7 +104,8 @@ enum {
|
|||
#define PLAYBACK_BUFFER_SECONDS (.5)
|
||||
#define PLAYBACK_BUFFER_FRAGMENTS (10)
|
||||
#define RECORD_BUFFER_SECONDS (5)
|
||||
#define RECORD_BUFFER_FRAGMENTS (100)
|
||||
#define DEFAULT_SINK_LATENCY (300*PA_USEC_PER_MSEC)
|
||||
#define DEFAULT_SOURCE_LATENCY (300*PA_USEC_PER_MSEC)
|
||||
|
||||
static void connection_unlink(connection *c) {
|
||||
pa_assert(c);
|
||||
|
|
@ -140,8 +144,6 @@ static void connection_free(pa_object *o) {
|
|||
connection *c = CONNECTION(o);
|
||||
pa_assert(c);
|
||||
|
||||
connection_unref(c);
|
||||
|
||||
if (c->playback.current_memblock)
|
||||
pa_memblock_unref(c->playback.current_memblock);
|
||||
|
||||
|
|
@ -158,27 +160,33 @@ static int do_read(connection *c) {
|
|||
ssize_t r;
|
||||
size_t l;
|
||||
void *p;
|
||||
size_t space;
|
||||
|
||||
connection_assert_ref(c);
|
||||
|
||||
if (!c->sink_input || (l = pa_atomic_load(&c->playback.missing)) <= 0)
|
||||
return 0;
|
||||
|
||||
if (l > c->playback.fragment_size)
|
||||
l = c->playback.fragment_size;
|
||||
if (c->playback.current_memblock) {
|
||||
|
||||
if (c->playback.current_memblock)
|
||||
if (pa_memblock_get_length(c->playback.current_memblock) - c->playback.memblock_index < l) {
|
||||
space = pa_memblock_get_length(c->playback.current_memblock) - c->playback.memblock_index;
|
||||
|
||||
if (space <= 0) {
|
||||
pa_memblock_unref(c->playback.current_memblock);
|
||||
c->playback.current_memblock = NULL;
|
||||
c->playback.memblock_index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!c->playback.current_memblock) {
|
||||
pa_assert_se(c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, l));
|
||||
pa_assert_se(c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, 0));
|
||||
c->playback.memblock_index = 0;
|
||||
|
||||
space = pa_memblock_get_length(c->playback.current_memblock);
|
||||
}
|
||||
|
||||
if (l > space)
|
||||
l = space;
|
||||
|
||||
p = pa_memblock_acquire(c->playback.current_memblock);
|
||||
r = pa_iochannel_read(c->io, (uint8_t*) p + c->playback.memblock_index, l);
|
||||
pa_memblock_release(c->playback.current_memblock);
|
||||
|
|
@ -248,16 +256,16 @@ static void do_work(connection *c) {
|
|||
if (c->dead)
|
||||
return;
|
||||
|
||||
if (pa_iochannel_is_readable(c->io)) {
|
||||
if (pa_iochannel_is_readable(c->io))
|
||||
if (do_read(c) < 0)
|
||||
goto fail;
|
||||
} else if (pa_iochannel_is_hungup(c->io))
|
||||
|
||||
if (!c->sink_input && pa_iochannel_is_hungup(c->io))
|
||||
goto fail;
|
||||
|
||||
if (pa_iochannel_is_writable(c->io)) {
|
||||
if (pa_iochannel_is_writable(c->io))
|
||||
if (do_write(c) < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
|
|
@ -266,7 +274,7 @@ fail:
|
|||
if (c->sink_input) {
|
||||
|
||||
/* If there is a sink input, we first drain what we already have read before shutting down the connection */
|
||||
c->dead = 1;
|
||||
c->dead = TRUE;
|
||||
|
||||
pa_iochannel_free(c->io);
|
||||
c->io = NULL;
|
||||
|
|
@ -318,15 +326,19 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
|
|||
/* New data from the main loop */
|
||||
pa_memblockq_push_align(c->input_memblockq, chunk);
|
||||
|
||||
if (pa_memblockq_is_readable(c->input_memblockq) && c->playback.underrun) {
|
||||
pa_log_debug("Requesting rewind due to end of underrun.");
|
||||
pa_sink_input_request_rewind(c->sink_input, 0, FALSE, TRUE);
|
||||
}
|
||||
|
||||
/* pa_log("got data, %u", pa_memblockq_get_length(c->input_memblockq)); */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
case SINK_INPUT_MESSAGE_DISABLE_PREBUF: {
|
||||
case SINK_INPUT_MESSAGE_DISABLE_PREBUF:
|
||||
pa_memblockq_prebuf_disable(c->input_memblockq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
|
||||
pa_usec_t *r = userdata;
|
||||
|
|
@ -343,43 +355,62 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
|
|||
}
|
||||
|
||||
/* Called from thread context */
|
||||
static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
|
||||
static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
|
||||
connection *c;
|
||||
int r;
|
||||
|
||||
pa_assert(i);
|
||||
pa_sink_input_assert_ref(i);
|
||||
c = CONNECTION(i->userdata);
|
||||
connection_assert_ref(c);
|
||||
pa_assert(chunk);
|
||||
|
||||
r = pa_memblockq_peek(c->input_memblockq, chunk);
|
||||
if (pa_memblockq_peek(c->input_memblockq, chunk) < 0) {
|
||||
|
||||
/* pa_log("peeked %u %i", r >= 0 ? chunk->length: 0, r); */
|
||||
c->playback.underrun = TRUE;
|
||||
|
||||
if (c->dead && r < 0)
|
||||
pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_UNLINK_CONNECTION, NULL, 0, NULL, NULL);
|
||||
if (c->dead && pa_sink_input_safe_to_remove(i))
|
||||
pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_UNLINK_CONNECTION, NULL, 0, NULL, NULL);
|
||||
|
||||
return r;
|
||||
return -1;
|
||||
} else {
|
||||
size_t m;
|
||||
|
||||
c->playback.underrun = FALSE;
|
||||
|
||||
pa_memblockq_drop(c->input_memblockq, chunk->length);
|
||||
m = pa_memblockq_pop_missing(c->input_memblockq);
|
||||
|
||||
if (m > 0)
|
||||
if (pa_atomic_add(&c->playback.missing, m) <= 0)
|
||||
pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Called from thread context */
|
||||
static void sink_input_drop_cb(pa_sink_input *i, size_t length) {
|
||||
static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
|
||||
connection *c;
|
||||
size_t old, new;
|
||||
|
||||
pa_assert(i);
|
||||
pa_sink_input_assert_ref(i);
|
||||
c = CONNECTION(i->userdata);
|
||||
connection_assert_ref(c);
|
||||
pa_assert(length);
|
||||
|
||||
old = pa_memblockq_missing(c->input_memblockq);
|
||||
pa_memblockq_drop(c->input_memblockq, length);
|
||||
new = pa_memblockq_missing(c->input_memblockq);
|
||||
/* If we are in an underrun, then we don't rewind */
|
||||
if (i->thread_info.underrun_for > 0)
|
||||
return;
|
||||
|
||||
if (new > old) {
|
||||
if (pa_atomic_add(&c->playback.missing, new - old) <= 0)
|
||||
pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL);
|
||||
}
|
||||
pa_memblockq_rewind(c->input_memblockq, nbytes);
|
||||
}
|
||||
|
||||
/* Called from thread context */
|
||||
static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
|
||||
connection *c;
|
||||
|
||||
pa_sink_input_assert_ref(i);
|
||||
c = CONNECTION(i->userdata);
|
||||
connection_assert_ref(c);
|
||||
|
||||
pa_memblockq_set_maxrewind(c->input_memblockq, nbytes);
|
||||
}
|
||||
|
||||
/* Called from main context */
|
||||
|
|
@ -395,7 +426,7 @@ static void sink_input_kill_cb(pa_sink_input *i) {
|
|||
static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
|
||||
connection *c;
|
||||
|
||||
pa_assert(o);
|
||||
pa_source_output_assert_ref(o);
|
||||
c = CONNECTION(o->userdata);
|
||||
pa_assert(c);
|
||||
pa_assert(chunk);
|
||||
|
|
@ -414,7 +445,7 @@ static void source_output_kill_cb(pa_source_output *o) {
|
|||
static pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
|
||||
connection*c;
|
||||
|
||||
pa_assert(o);
|
||||
pa_source_output_assert_ref(o);
|
||||
c = CONNECTION(o->userdata);
|
||||
pa_assert(c);
|
||||
|
||||
|
|
@ -449,7 +480,7 @@ static void io_callback(pa_iochannel*io, void *userdata) {
|
|||
static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) {
|
||||
pa_protocol_simple *p = userdata;
|
||||
connection *c = NULL;
|
||||
char cname[256];
|
||||
char cname[256], pname[128];
|
||||
|
||||
pa_assert(s);
|
||||
pa_assert(io);
|
||||
|
|
@ -471,49 +502,64 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
|
|||
c->protocol = p;
|
||||
c->playback.current_memblock = NULL;
|
||||
c->playback.memblock_index = 0;
|
||||
c->playback.fragment_size = 0;
|
||||
c->dead = 0;
|
||||
c->dead = FALSE;
|
||||
c->playback.underrun = TRUE;
|
||||
pa_atomic_store(&c->playback.missing, 0);
|
||||
|
||||
pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname));
|
||||
pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname));
|
||||
pa_snprintf(cname, sizeof(cname), "Simple client (%s)", pname);
|
||||
pa_assert_se(c->client = pa_client_new(p->core, __FILE__, cname));
|
||||
c->client->owner = p->module;
|
||||
pa_proplist_sets(c->client->proplist, "simple-protocol.peer", pname);
|
||||
c->client->module = p->module;
|
||||
c->client->kill = client_kill_cb;
|
||||
c->client->userdata = c;
|
||||
|
||||
if (p->mode & PLAYBACK) {
|
||||
pa_sink_input_new_data data;
|
||||
size_t l;
|
||||
pa_sink *sink;
|
||||
|
||||
if (!(sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, TRUE))) {
|
||||
pa_log("Failed to get sink.");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
pa_sink_input_new_data_init(&data);
|
||||
data.driver = __FILE__;
|
||||
data.name = c->client->name;
|
||||
pa_sink_input_new_data_set_sample_spec(&data, &p->sample_spec);
|
||||
data.module = p->module;
|
||||
data.client = c->client;
|
||||
data.sink = sink;
|
||||
pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist);
|
||||
pa_sink_input_new_data_set_sample_spec(&data, &p->sample_spec);
|
||||
|
||||
if (!(c->sink_input = pa_sink_input_new(p->core, &data, 0))) {
|
||||
c->sink_input = pa_sink_input_new(p->core, &data, 0);
|
||||
pa_sink_input_new_data_done(&data);
|
||||
|
||||
if (!c->sink_input) {
|
||||
pa_log("Failed to create sink input.");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
c->sink_input->parent.process_msg = sink_input_process_msg;
|
||||
c->sink_input->peek = sink_input_peek_cb;
|
||||
c->sink_input->drop = sink_input_drop_cb;
|
||||
c->sink_input->pop = sink_input_pop_cb;
|
||||
c->sink_input->process_rewind = sink_input_process_rewind_cb;
|
||||
c->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
|
||||
c->sink_input->kill = sink_input_kill_cb;
|
||||
c->sink_input->userdata = c;
|
||||
|
||||
pa_sink_input_set_requested_latency(c->sink_input, DEFAULT_SINK_LATENCY);
|
||||
|
||||
l = (size_t) (pa_bytes_per_second(&p->sample_spec)*PLAYBACK_BUFFER_SECONDS);
|
||||
c->input_memblockq = pa_memblockq_new(
|
||||
0,
|
||||
l,
|
||||
0,
|
||||
l,
|
||||
pa_frame_size(&p->sample_spec),
|
||||
(size_t) -1,
|
||||
l/PLAYBACK_BUFFER_FRAGMENTS,
|
||||
0,
|
||||
NULL);
|
||||
pa_iochannel_socket_set_rcvbuf(io, l/PLAYBACK_BUFFER_FRAGMENTS*5);
|
||||
c->playback.fragment_size = l/PLAYBACK_BUFFER_FRAGMENTS;
|
||||
pa_iochannel_socket_set_rcvbuf(io, l);
|
||||
|
||||
pa_atomic_store(&c->playback.missing, pa_memblockq_missing(c->input_memblockq));
|
||||
|
||||
|
|
@ -523,15 +569,25 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
|
|||
if (p->mode & RECORD) {
|
||||
pa_source_output_new_data data;
|
||||
size_t l;
|
||||
pa_source *source;
|
||||
|
||||
if (!(source = pa_namereg_get(c->protocol->core, c->protocol->source_name, PA_NAMEREG_SOURCE, TRUE))) {
|
||||
pa_log("Failed to get source.");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
pa_source_output_new_data_init(&data);
|
||||
data.driver = __FILE__;
|
||||
data.name = c->client->name;
|
||||
pa_source_output_new_data_set_sample_spec(&data, &p->sample_spec);
|
||||
data.module = p->module;
|
||||
data.client = c->client;
|
||||
data.source = source;
|
||||
pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist);
|
||||
pa_source_output_new_data_set_sample_spec(&data, &p->sample_spec);
|
||||
|
||||
if (!(c->source_output = pa_source_output_new(p->core, &data, 0))) {
|
||||
c->source_output = pa_source_output_new(p->core, &data, 0);
|
||||
pa_source_output_new_data_done(&data);
|
||||
|
||||
if (!c->source_output) {
|
||||
pa_log("Failed to create source output.");
|
||||
goto fail;
|
||||
}
|
||||
|
|
@ -540,6 +596,8 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
|
|||
c->source_output->get_latency = source_output_get_latency_cb;
|
||||
c->source_output->userdata = c;
|
||||
|
||||
pa_source_output_set_requested_latency(c->source_output, DEFAULT_SOURCE_LATENCY);
|
||||
|
||||
l = (size_t) (pa_bytes_per_second(&p->sample_spec)*RECORD_BUFFER_SECONDS);
|
||||
c->output_memblockq = pa_memblockq_new(
|
||||
0,
|
||||
|
|
@ -548,8 +606,9 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
|
|||
pa_frame_size(&p->sample_spec),
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
NULL);
|
||||
pa_iochannel_socket_set_sndbuf(io, l/RECORD_BUFFER_FRAGMENTS*2);
|
||||
pa_iochannel_socket_set_sndbuf(io, l);
|
||||
|
||||
pa_source_output_put(c->source_output);
|
||||
}
|
||||
|
|
@ -570,12 +629,13 @@ pa_protocol_simple* pa_protocol_simple_new(pa_core *core, pa_socket_server *serv
|
|||
|
||||
pa_assert(core);
|
||||
pa_assert(server);
|
||||
pa_assert(m);
|
||||
pa_assert(ma);
|
||||
|
||||
p = pa_xnew0(pa_protocol_simple, 1);
|
||||
p->module = m;
|
||||
p->core = core;
|
||||
p->server = server;
|
||||
p->server = pa_socket_server_ref(server);
|
||||
p->connections = pa_idxset_new(NULL, NULL);
|
||||
|
||||
p->sample_spec = core->default_sample_spec;
|
||||
|
|
@ -594,7 +654,7 @@ pa_protocol_simple* pa_protocol_simple_new(pa_core *core, pa_socket_server *serv
|
|||
}
|
||||
p->mode = enable ? RECORD : 0;
|
||||
|
||||
enable = 1;
|
||||
enable = TRUE;
|
||||
if (pa_modargs_get_value_boolean(ma, "playback", &enable) < 0) {
|
||||
pa_log("playback= expects a numeric argument.");
|
||||
goto fail;
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ struct item_info {
|
|||
/* packet info */
|
||||
pa_packet *packet;
|
||||
#ifdef HAVE_CREDS
|
||||
int with_creds;
|
||||
pa_bool_t with_creds;
|
||||
pa_creds creds;
|
||||
#endif
|
||||
|
||||
|
|
@ -121,7 +121,7 @@ struct pa_pstream {
|
|||
|
||||
pa_queue *send_queue;
|
||||
|
||||
int dead;
|
||||
pa_bool_t dead;
|
||||
|
||||
struct {
|
||||
pa_pstream_descriptor descriptor;
|
||||
|
|
@ -141,7 +141,7 @@ struct pa_pstream {
|
|||
size_t index;
|
||||
} read;
|
||||
|
||||
int use_shm;
|
||||
pa_bool_t use_shm;
|
||||
pa_memimport *import;
|
||||
pa_memexport *export;
|
||||
|
||||
|
|
@ -167,7 +167,7 @@ struct pa_pstream {
|
|||
|
||||
#ifdef HAVE_CREDS
|
||||
pa_creds read_creds, write_creds;
|
||||
int read_creds_valid, send_creds_now;
|
||||
pa_bool_t read_creds_valid, send_creds_now;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
@ -239,7 +239,7 @@ pa_pstream *pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_mempool *poo
|
|||
PA_REFCNT_INIT(p);
|
||||
p->io = io;
|
||||
pa_iochannel_set_callback(io, io_callback, p);
|
||||
p->dead = 0;
|
||||
p->dead = FALSE;
|
||||
|
||||
p->mainloop = m;
|
||||
p->defer_event = m->defer_new(m, defer_callback, p);
|
||||
|
|
@ -269,18 +269,18 @@ pa_pstream *pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_mempool *poo
|
|||
|
||||
p->mempool = pool;
|
||||
|
||||
p->use_shm = 0;
|
||||
p->use_shm = FALSE;
|
||||
p->export = NULL;
|
||||
|
||||
/* We do importing unconditionally */
|
||||
p->import = pa_memimport_new(p->mempool, memimport_release_cb, p);
|
||||
|
||||
pa_iochannel_socket_set_rcvbuf(io, 1024*8);
|
||||
pa_iochannel_socket_set_sndbuf(io, 1024*8);
|
||||
pa_iochannel_socket_set_rcvbuf(io, pa_mempool_block_size_max(p->mempool));
|
||||
pa_iochannel_socket_set_sndbuf(io, pa_mempool_block_size_max(p->mempool));
|
||||
|
||||
#ifdef HAVE_CREDS
|
||||
p->send_creds_now = 0;
|
||||
p->read_creds_valid = 0;
|
||||
p->send_creds_now = FALSE;
|
||||
p->read_creds_valid = FALSE;
|
||||
#endif
|
||||
return p;
|
||||
}
|
||||
|
|
@ -374,7 +374,7 @@ void pa_pstream_send_memblock(pa_pstream*p, uint32_t channel, int64_t offset, pa
|
|||
i = pa_xnew(struct item_info, 1);
|
||||
i->type = PA_PSTREAM_ITEM_MEMBLOCK;
|
||||
|
||||
n = MIN(length, bsm);
|
||||
n = PA_MIN(length, bsm);
|
||||
i->chunk.index = chunk->index + idx;
|
||||
i->chunk.length = n;
|
||||
i->chunk.memblock = pa_memblock_ref(chunk->memblock);
|
||||
|
|
@ -383,7 +383,7 @@ void pa_pstream_send_memblock(pa_pstream*p, uint32_t channel, int64_t offset, pa
|
|||
i->offset = offset;
|
||||
i->seek_mode = seek_mode;
|
||||
#ifdef HAVE_CREDS
|
||||
i->with_creds = 0;
|
||||
i->with_creds = FALSE;
|
||||
#endif
|
||||
|
||||
pa_queue_push(p->send_queue, i);
|
||||
|
|
@ -410,7 +410,7 @@ void pa_pstream_send_release(pa_pstream *p, uint32_t block_id) {
|
|||
item->type = PA_PSTREAM_ITEM_SHMRELEASE;
|
||||
item->block_id = block_id;
|
||||
#ifdef HAVE_CREDS
|
||||
item->with_creds = 0;
|
||||
item->with_creds = FALSE;
|
||||
#endif
|
||||
|
||||
pa_queue_push(p->send_queue, item);
|
||||
|
|
@ -447,7 +447,7 @@ void pa_pstream_send_revoke(pa_pstream *p, uint32_t block_id) {
|
|||
item->type = PA_PSTREAM_ITEM_SHMREVOKE;
|
||||
item->block_id = block_id;
|
||||
#ifdef HAVE_CREDS
|
||||
item->with_creds = 0;
|
||||
item->with_creds = FALSE;
|
||||
#endif
|
||||
|
||||
pa_queue_push(p->send_queue, item);
|
||||
|
|
@ -504,7 +504,7 @@ static void prepare_next_write_item(pa_pstream *p) {
|
|||
|
||||
} else {
|
||||
uint32_t flags;
|
||||
int send_payload = 1;
|
||||
pa_bool_t send_payload = TRUE;
|
||||
|
||||
pa_assert(p->write.current->type == PA_PSTREAM_ITEM_MEMBLOCK);
|
||||
pa_assert(p->write.current->chunk.memblock);
|
||||
|
|
@ -529,7 +529,7 @@ static void prepare_next_write_item(pa_pstream *p) {
|
|||
&length) >= 0) {
|
||||
|
||||
flags |= PA_FLAG_SHMDATA;
|
||||
send_payload = 0;
|
||||
send_payload = FALSE;
|
||||
|
||||
p->write.shm_info[PA_PSTREAM_SHM_BLOCKID] = htonl(block_id);
|
||||
p->write.shm_info[PA_PSTREAM_SHM_SHMID] = htonl(shm_id);
|
||||
|
|
@ -599,7 +599,7 @@ static int do_write(pa_pstream *p) {
|
|||
if ((r = pa_iochannel_write_with_creds(p->io, d, l, &p->write_creds)) < 0)
|
||||
goto fail;
|
||||
|
||||
p->send_creds_now = 0;
|
||||
p->send_creds_now = FALSE;
|
||||
} else
|
||||
#endif
|
||||
|
||||
|
|
@ -875,7 +875,7 @@ frame_done:
|
|||
p->read.data = NULL;
|
||||
|
||||
#ifdef HAVE_CREDS
|
||||
p->read_creds_valid = 0;
|
||||
p->read_creds_valid = FALSE;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
|
@ -935,14 +935,14 @@ void pa_pstream_set_revoke_callback(pa_pstream *p, pa_pstream_block_id_cb_t cb,
|
|||
p->release_callback_userdata = userdata;
|
||||
}
|
||||
|
||||
int pa_pstream_is_pending(pa_pstream *p) {
|
||||
int b;
|
||||
pa_bool_t pa_pstream_is_pending(pa_pstream *p) {
|
||||
pa_bool_t b;
|
||||
|
||||
pa_assert(p);
|
||||
pa_assert(PA_REFCNT_VALUE(p) > 0);
|
||||
|
||||
if (p->dead)
|
||||
b = 0;
|
||||
b = FALSE;
|
||||
else
|
||||
b = p->write.current || !pa_queue_is_empty(p->send_queue);
|
||||
|
||||
|
|
@ -971,7 +971,7 @@ void pa_pstream_unlink(pa_pstream *p) {
|
|||
if (p->dead)
|
||||
return;
|
||||
|
||||
p->dead = 1;
|
||||
p->dead = TRUE;
|
||||
|
||||
if (p->import) {
|
||||
pa_memimport_free(p->import);
|
||||
|
|
@ -999,7 +999,7 @@ void pa_pstream_unlink(pa_pstream *p) {
|
|||
p->recieve_memblock_callback = NULL;
|
||||
}
|
||||
|
||||
void pa_pstream_use_shm(pa_pstream *p, int enable) {
|
||||
void pa_pstream_enable_shm(pa_pstream *p, pa_bool_t enable) {
|
||||
pa_assert(p);
|
||||
pa_assert(PA_REFCNT_VALUE(p) > 0);
|
||||
|
||||
|
|
@ -1018,3 +1018,10 @@ void pa_pstream_use_shm(pa_pstream *p, int enable) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pa_bool_t pa_pstream_get_shm(pa_pstream *p) {
|
||||
pa_assert(p);
|
||||
pa_assert(PA_REFCNT_VALUE(p) > 0);
|
||||
|
||||
return p->use_shm;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@
|
|||
#include <pulsecore/iochannel.h>
|
||||
#include <pulsecore/memchunk.h>
|
||||
#include <pulsecore/creds.h>
|
||||
#include <pulsecore/macro.h>
|
||||
|
||||
typedef struct pa_pstream pa_pstream;
|
||||
|
||||
|
|
@ -44,8 +45,11 @@ typedef void (*pa_pstream_notify_cb_t)(pa_pstream *p, void *userdata);
|
|||
typedef void (*pa_pstream_block_id_cb_t)(pa_pstream *p, uint32_t block_id, void *userdata);
|
||||
|
||||
pa_pstream* pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_mempool *p);
|
||||
void pa_pstream_unref(pa_pstream*p);
|
||||
|
||||
pa_pstream* pa_pstream_ref(pa_pstream*p);
|
||||
void pa_pstream_unref(pa_pstream*p);
|
||||
|
||||
void pa_pstream_unlink(pa_pstream *p);
|
||||
|
||||
void pa_pstream_send_packet(pa_pstream*p, pa_packet *packet, const pa_creds *creds);
|
||||
void pa_pstream_send_memblock(pa_pstream*p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk);
|
||||
|
|
@ -59,10 +63,9 @@ void pa_pstream_set_die_callback(pa_pstream *p, pa_pstream_notify_cb_t cb, void
|
|||
void pa_pstream_set_release_callback(pa_pstream *p, pa_pstream_block_id_cb_t cb, void *userdata);
|
||||
void pa_pstream_set_revoke_callback(pa_pstream *p, pa_pstream_block_id_cb_t cb, void *userdata);
|
||||
|
||||
int pa_pstream_is_pending(pa_pstream *p);
|
||||
pa_bool_t pa_pstream_is_pending(pa_pstream *p);
|
||||
|
||||
void pa_pstream_use_shm(pa_pstream *p, int enable);
|
||||
|
||||
void pa_pstream_unlink(pa_pstream *p);
|
||||
void pa_pstream_enable_shm(pa_pstream *p, pa_bool_t enable);
|
||||
pa_bool_t pa_pstream_get_shm(pa_pstream *p);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -32,6 +32,9 @@
|
|||
#define PA_REFCNT_INIT(p) \
|
||||
pa_atomic_store(&(p)->_ref, 1)
|
||||
|
||||
#define PA_REFCNT_INIT_ZERO(p) \
|
||||
pa_atomic_store(&(p)->_ref, 0)
|
||||
|
||||
#define PA_REFCNT_INC(p) \
|
||||
pa_atomic_inc(&(p)->_ref)
|
||||
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@
|
|||
#include "resampler.h"
|
||||
|
||||
/* Number of samples of extra space we allow the resamplers to return */
|
||||
#define EXTRA_SAMPLES 128
|
||||
#define EXTRA_FRAMES 128
|
||||
|
||||
struct pa_resampler {
|
||||
pa_resample_method_t method;
|
||||
|
|
@ -79,6 +79,15 @@ struct pa_resampler {
|
|||
unsigned i_counter;
|
||||
} trivial;
|
||||
|
||||
struct { /* data specific to the peak finder pseudo resampler */
|
||||
unsigned o_counter;
|
||||
unsigned i_counter;
|
||||
|
||||
float max_f[PA_CHANNELS_MAX];
|
||||
int16_t max_i[PA_CHANNELS_MAX];
|
||||
|
||||
} peaks;
|
||||
|
||||
#ifdef HAVE_LIBSAMPLERATE
|
||||
struct { /* data specific to libsamplerate */
|
||||
SRC_STATE *state;
|
||||
|
|
@ -99,6 +108,7 @@ static int copy_init(pa_resampler *r);
|
|||
static int trivial_init(pa_resampler*r);
|
||||
static int speex_init(pa_resampler*r);
|
||||
static int ffmpeg_init(pa_resampler*r);
|
||||
static int peaks_init(pa_resampler*r);
|
||||
#ifdef HAVE_LIBSAMPLERATE
|
||||
static int libsamplerate_init(pa_resampler*r);
|
||||
#endif
|
||||
|
|
@ -144,7 +154,8 @@ static int (* const init_table[])(pa_resampler*r) = {
|
|||
[PA_RESAMPLER_SPEEX_FIXED_BASE+10] = speex_init,
|
||||
[PA_RESAMPLER_FFMPEG] = ffmpeg_init,
|
||||
[PA_RESAMPLER_AUTO] = NULL,
|
||||
[PA_RESAMPLER_COPY] = copy_init
|
||||
[PA_RESAMPLER_COPY] = copy_init,
|
||||
[PA_RESAMPLER_PEAKS] = peaks_init,
|
||||
};
|
||||
|
||||
static inline size_t sample_size(pa_sample_format_t f) {
|
||||
|
|
@ -242,9 +253,9 @@ pa_resampler* pa_resampler_new(
|
|||
if ((method >= PA_RESAMPLER_SPEEX_FIXED_BASE && method <= PA_RESAMPLER_SPEEX_FIXED_MAX) ||
|
||||
(method == PA_RESAMPLER_FFMPEG))
|
||||
r->work_format = PA_SAMPLE_S16NE;
|
||||
else if (method == PA_RESAMPLER_TRIVIAL || method == PA_RESAMPLER_COPY) {
|
||||
else if (method == PA_RESAMPLER_TRIVIAL || method == PA_RESAMPLER_COPY || method == PA_RESAMPLER_PEAKS) {
|
||||
|
||||
if (r->map_required || a->format != b->format) {
|
||||
if (r->map_required || a->format != b->format || method == PA_RESAMPLER_PEAKS) {
|
||||
|
||||
if (a->format == PA_SAMPLE_S32NE || a->format == PA_SAMPLE_S32RE ||
|
||||
a->format == PA_SAMPLE_FLOAT32NE || a->format == PA_SAMPLE_FLOAT32RE ||
|
||||
|
|
@ -347,6 +358,12 @@ size_t pa_resampler_request(pa_resampler *r, size_t out_length) {
|
|||
return (((out_length / r->o_fz)*r->i_ss.rate)/r->o_ss.rate) * r->i_fz;
|
||||
}
|
||||
|
||||
size_t pa_resampler_result(pa_resampler *r, size_t in_length) {
|
||||
pa_assert(r);
|
||||
|
||||
return (((in_length / r->i_fz)*r->o_ss.rate)/r->i_ss.rate) * r->o_fz;
|
||||
}
|
||||
|
||||
size_t pa_resampler_max_block_size(pa_resampler *r) {
|
||||
size_t block_size_max;
|
||||
pa_sample_spec ss;
|
||||
|
|
@ -358,22 +375,17 @@ size_t pa_resampler_max_block_size(pa_resampler *r) {
|
|||
|
||||
/* We deduce the "largest" sample spec we're using during the
|
||||
* conversion */
|
||||
ss = r->i_ss;
|
||||
if (r->o_ss.channels > ss.channels)
|
||||
ss.channels = r->o_ss.channels;
|
||||
ss.channels = PA_MAX(r->i_ss.channels, r->o_ss.channels);
|
||||
|
||||
/* We silently assume that the format enum is ordered by size */
|
||||
if (r->o_ss.format > ss.format)
|
||||
ss.format = r->o_ss.format;
|
||||
if (r->work_format > ss.format)
|
||||
ss.format = r->work_format;
|
||||
ss.format = PA_MAX(r->i_ss.format, r->o_ss.format);
|
||||
ss.format = PA_MAX(ss.format, r->work_format);
|
||||
|
||||
if (r->o_ss.rate > ss.rate)
|
||||
ss.rate = r->o_ss.rate;
|
||||
ss.rate = PA_MAX(r->i_ss.rate, r->o_ss.rate);
|
||||
|
||||
fs = pa_frame_size(&ss);
|
||||
|
||||
return (((block_size_max/fs + EXTRA_SAMPLES)*r->i_ss.rate)/ss.rate)*r->i_fz;
|
||||
return (((block_size_max/fs - EXTRA_FRAMES)*r->i_ss.rate)/ss.rate)*r->i_fz;
|
||||
}
|
||||
|
||||
void pa_resampler_reset(pa_resampler *r) {
|
||||
|
|
@ -420,7 +432,8 @@ static const char * const resample_methods[] = {
|
|||
"speex-fixed-10",
|
||||
"ffmpeg",
|
||||
"auto",
|
||||
"copy"
|
||||
"copy",
|
||||
"peaks"
|
||||
};
|
||||
|
||||
const char *pa_resample_method_to_string(pa_resample_method_t m) {
|
||||
|
|
@ -1069,7 +1082,7 @@ static pa_memchunk *resample(pa_resampler *r, pa_memchunk *input) {
|
|||
in_n_samples = input->length / r->w_sz;
|
||||
in_n_frames = in_n_samples / r->o_ss.channels;
|
||||
|
||||
out_n_frames = ((in_n_frames*r->o_ss.rate)/r->i_ss.rate)+EXTRA_SAMPLES;
|
||||
out_n_frames = ((in_n_frames*r->o_ss.rate)/r->i_ss.rate)+EXTRA_FRAMES;
|
||||
out_n_samples = out_n_frames * r->o_ss.channels;
|
||||
|
||||
r->buf3.index = 0;
|
||||
|
|
@ -1400,6 +1413,113 @@ static int trivial_init(pa_resampler*r) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Peak finder implementation */
|
||||
|
||||
static void peaks_resample(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) {
|
||||
size_t fz;
|
||||
unsigned o_index;
|
||||
void *src, *dst;
|
||||
unsigned start = 0;
|
||||
|
||||
pa_assert(r);
|
||||
pa_assert(input);
|
||||
pa_assert(output);
|
||||
pa_assert(out_n_frames);
|
||||
|
||||
fz = r->w_sz * r->o_ss.channels;
|
||||
|
||||
src = (uint8_t*) pa_memblock_acquire(input->memblock) + input->index;
|
||||
dst = (uint8_t*) pa_memblock_acquire(output->memblock) + output->index;
|
||||
|
||||
for (o_index = 0;; o_index++, r->peaks.o_counter++) {
|
||||
unsigned j;
|
||||
|
||||
j = ((r->peaks.o_counter * r->i_ss.rate) / r->o_ss.rate);
|
||||
j = j > r->peaks.i_counter ? j - r->peaks.i_counter : 0;
|
||||
|
||||
if (j >= in_n_frames)
|
||||
break;
|
||||
|
||||
pa_assert(o_index * fz < pa_memblock_get_length(output->memblock));
|
||||
|
||||
if (r->work_format == PA_SAMPLE_S16NE) {
|
||||
unsigned i, c;
|
||||
int16_t *s = (int16_t*) ((uint8_t*) src + fz * j);
|
||||
int16_t *d = (int16_t*) ((uint8_t*) dst + fz * o_index);
|
||||
|
||||
for (i = start; i <= j; i++)
|
||||
for (c = 0; c < r->o_ss.channels; c++, s++) {
|
||||
int16_t n;
|
||||
|
||||
n = *s < 0 ? -*s : *s;
|
||||
|
||||
if (n > r->peaks.max_i[c])
|
||||
r->peaks.max_i[c] = n;
|
||||
}
|
||||
|
||||
for (c = 0; c < r->o_ss.channels; c++, d++)
|
||||
*d = r->peaks.max_i[c];
|
||||
|
||||
memset(r->peaks.max_i, 0, sizeof(r->peaks.max_i));
|
||||
} else {
|
||||
unsigned i, c;
|
||||
float *s = (float*) ((uint8_t*) src + fz * j);
|
||||
float *d = (float*) ((uint8_t*) dst + fz * o_index);
|
||||
|
||||
pa_assert(r->work_format == PA_SAMPLE_FLOAT32NE);
|
||||
|
||||
for (i = start; i <= j; i++)
|
||||
for (c = 0; c < r->o_ss.channels; c++, s++) {
|
||||
float n = fabsf(*s);
|
||||
|
||||
if (n > r->peaks.max_f[c])
|
||||
r->peaks.max_f[c] = n;
|
||||
}
|
||||
|
||||
for (c = 0; c < r->o_ss.channels; c++, d++)
|
||||
*d = r->peaks.max_f[c];
|
||||
|
||||
memset(r->peaks.max_f, 0, sizeof(r->peaks.max_f));
|
||||
}
|
||||
}
|
||||
|
||||
pa_memblock_release(input->memblock);
|
||||
pa_memblock_release(output->memblock);
|
||||
|
||||
*out_n_frames = o_index;
|
||||
|
||||
r->peaks.i_counter += in_n_frames;
|
||||
|
||||
/* Normalize counters */
|
||||
while (r->peaks.i_counter >= r->i_ss.rate) {
|
||||
pa_assert(r->peaks.o_counter >= r->o_ss.rate);
|
||||
|
||||
r->peaks.i_counter -= r->i_ss.rate;
|
||||
r->peaks.o_counter -= r->o_ss.rate;
|
||||
}
|
||||
}
|
||||
|
||||
static void peaks_update_rates_or_reset(pa_resampler *r) {
|
||||
pa_assert(r);
|
||||
|
||||
r->peaks.i_counter = 0;
|
||||
r->peaks.o_counter = 0;
|
||||
}
|
||||
|
||||
static int peaks_init(pa_resampler*r) {
|
||||
pa_assert(r);
|
||||
|
||||
r->peaks.o_counter = r->peaks.i_counter = 0;
|
||||
memset(r->peaks.max_i, 0, sizeof(r->peaks.max_i));
|
||||
memset(r->peaks.max_f, 0, sizeof(r->peaks.max_f));
|
||||
|
||||
r->impl_resample = peaks_resample;
|
||||
r->impl_update_rates = peaks_update_rates_or_reset;
|
||||
r->impl_reset = peaks_update_rates_or_reset;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*** ffmpeg based implementation ***/
|
||||
|
||||
static void ffmpeg_resample(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) {
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ typedef enum pa_resample_method {
|
|||
PA_RESAMPLER_FFMPEG,
|
||||
PA_RESAMPLER_AUTO, /* automatic select based on sample format */
|
||||
PA_RESAMPLER_COPY,
|
||||
PA_RESAMPLER_PEAKS,
|
||||
PA_RESAMPLER_MAX
|
||||
} pa_resample_method_t;
|
||||
|
||||
|
|
@ -69,6 +70,9 @@ void pa_resampler_free(pa_resampler *r);
|
|||
/* Returns the size of an input memory block which is required to return the specified amount of output data */
|
||||
size_t pa_resampler_request(pa_resampler *r, size_t out_length);
|
||||
|
||||
/* Inverse of pa_resampler_request() */
|
||||
size_t pa_resampler_result(pa_resampler *r, size_t in_length);
|
||||
|
||||
/* Returns the maximum size of input blocks we can process without needing bounce buffers larger than the mempool tile size. */
|
||||
size_t pa_resampler_max_block_size(pa_resampler *r);
|
||||
|
||||
|
|
|
|||
|
|
@ -96,3 +96,24 @@ pa_usec_t pa_rtclock_usec(void) {
|
|||
|
||||
return pa_timeval_load(pa_rtclock_get(&tv));
|
||||
}
|
||||
|
||||
struct timeval* pa_rtclock_from_wallclock(struct timeval *tv) {
|
||||
|
||||
#ifdef HAVE_CLOCK_GETTIME
|
||||
struct timeval wc_now, rt_now;
|
||||
|
||||
pa_gettimeofday(&wc_now);
|
||||
pa_rtclock_get(&rt_now);
|
||||
|
||||
pa_assert(tv);
|
||||
|
||||
if (pa_timeval_cmp(&wc_now, tv) < 0)
|
||||
pa_timeval_add(&rt_now, pa_timeval_diff(tv, &wc_now));
|
||||
else
|
||||
pa_timeval_sub(&rt_now, pa_timeval_diff(&wc_now, tv));
|
||||
|
||||
*tv = rt_now;
|
||||
#endif
|
||||
|
||||
return tv;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,4 +40,6 @@ pa_bool_t pa_rtclock_hrtimer(void);
|
|||
/* timer with a resolution better than this are considered high-resolution */
|
||||
#define PA_HRTIMER_THRESHOLD_USEC 10
|
||||
|
||||
struct timeval* pa_rtclock_from_wallclock(struct timeval *tv);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -63,7 +63,6 @@ struct pa_rtpoll {
|
|||
|
||||
pa_bool_t timer_enabled;
|
||||
struct timeval next_elapse;
|
||||
pa_usec_t period;
|
||||
|
||||
pa_bool_t scan_for_dead;
|
||||
pa_bool_t running, installed, rebuild_needed, quit;
|
||||
|
|
@ -72,6 +71,7 @@ struct pa_rtpoll {
|
|||
int rtsig;
|
||||
sigset_t sigset_unblocked;
|
||||
timer_t timer;
|
||||
pa_bool_t timer_armed;
|
||||
#ifdef __linux__
|
||||
pa_bool_t dont_use_ppoll;
|
||||
#endif
|
||||
|
|
@ -99,7 +99,7 @@ struct pa_rtpoll_item {
|
|||
|
||||
PA_STATIC_FLIST_DECLARE(items, 0, pa_xfree);
|
||||
|
||||
static void signal_handler_noop(int s) { }
|
||||
static void signal_handler_noop(int s) { /* write(2, "signal\n", 7); */ }
|
||||
|
||||
pa_rtpoll *pa_rtpoll_new(void) {
|
||||
pa_rtpoll *p;
|
||||
|
|
@ -131,6 +131,7 @@ pa_rtpoll *pa_rtpoll_new(void) {
|
|||
p->rtsig = -1;
|
||||
sigemptyset(&p->sigset_unblocked);
|
||||
p->timer = (timer_t) -1;
|
||||
p->timer_armed = FALSE;
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -139,7 +140,6 @@ pa_rtpoll *pa_rtpoll_new(void) {
|
|||
p->pollfd2 = pa_xnew(struct pollfd, p->n_pollfd_alloc);
|
||||
p->n_pollfd_used = 0;
|
||||
|
||||
p->period = 0;
|
||||
memset(&p->next_elapse, 0, sizeof(p->next_elapse));
|
||||
p->timer_enabled = FALSE;
|
||||
|
||||
|
|
@ -368,15 +368,13 @@ int pa_rtpoll_run(pa_rtpoll *p, pa_bool_t wait) {
|
|||
if (p->rebuild_needed)
|
||||
rtpoll_rebuild(p);
|
||||
|
||||
memset(&timeout, 0, sizeof(timeout));
|
||||
|
||||
/* Calculate timeout */
|
||||
if (!wait || p->quit) {
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 0;
|
||||
} else if (p->timer_enabled) {
|
||||
if (wait && !p->quit && p->timer_enabled) {
|
||||
struct timeval now;
|
||||
pa_rtclock_get(&now);
|
||||
|
||||
memset(&timeout, 0, sizeof(timeout));
|
||||
if (pa_timeval_cmp(&p->next_elapse, &now) > 0)
|
||||
pa_timeval_add(&timeout, pa_timeval_diff(&p->next_elapse, &now));
|
||||
}
|
||||
|
|
@ -391,14 +389,14 @@ int pa_rtpoll_run(pa_rtpoll *p, pa_bool_t wait) {
|
|||
struct timespec ts;
|
||||
ts.tv_sec = timeout.tv_sec;
|
||||
ts.tv_nsec = timeout.tv_usec * 1000;
|
||||
r = ppoll(p->pollfd, p->n_pollfd_used, p->timer_enabled ? &ts : NULL, p->rtsig < 0 ? NULL : &p->sigset_unblocked);
|
||||
r = ppoll(p->pollfd, p->n_pollfd_used, (!wait || p->quit || p->timer_enabled) ? &ts : NULL, p->rtsig < 0 ? NULL : &p->sigset_unblocked);
|
||||
}
|
||||
#ifdef __linux__
|
||||
else
|
||||
#endif
|
||||
|
||||
#endif
|
||||
r = poll(p->pollfd, p->n_pollfd_used, p->timer_enabled ? (timeout.tv_sec*1000) + (timeout.tv_usec / 1000) : -1);
|
||||
r = poll(p->pollfd, p->n_pollfd_used, (!wait || p->quit || p->timer_enabled) ? (timeout.tv_sec*1000) + (timeout.tv_usec / 1000) : -1);
|
||||
|
||||
if (r < 0) {
|
||||
if (errno == EAGAIN || errno == EINTR)
|
||||
|
|
@ -409,21 +407,6 @@ int pa_rtpoll_run(pa_rtpoll *p, pa_bool_t wait) {
|
|||
reset_all_revents(p);
|
||||
}
|
||||
|
||||
if (p->timer_enabled) {
|
||||
if (p->period > 0) {
|
||||
struct timeval now;
|
||||
pa_rtclock_get(&now);
|
||||
|
||||
pa_timeval_add(&p->next_elapse, p->period);
|
||||
|
||||
/* Guarantee that the next timeout will happen in the future */
|
||||
if (pa_timeval_cmp(&p->next_elapse, &now) < 0)
|
||||
pa_timeval_add(&p->next_elapse, (pa_timeval_diff(&now, &p->next_elapse) / p->period + 1) * p->period);
|
||||
|
||||
} else
|
||||
p->timer_enabled = FALSE;
|
||||
}
|
||||
|
||||
/* Let's tell everyone that we left the sleep */
|
||||
for (i = p->items; i && i->priority < PA_RTPOLL_NEVER; i = i->next) {
|
||||
|
||||
|
|
@ -481,26 +464,35 @@ static void update_timer(pa_rtpoll *p) {
|
|||
|
||||
if (p->timer != (timer_t) -1) {
|
||||
struct itimerspec its;
|
||||
memset(&its, 0, sizeof(its));
|
||||
struct timespec ts = { .tv_sec = 0, .tv_nsec = 0 };
|
||||
sigset_t ss;
|
||||
|
||||
if (p->timer_armed) {
|
||||
/* First disarm timer */
|
||||
memset(&its, 0, sizeof(its));
|
||||
pa_assert_se(timer_settime(p->timer, TIMER_ABSTIME, &its, NULL) == 0);
|
||||
|
||||
/* Remove a signal that might be waiting in the signal q */
|
||||
pa_assert_se(sigemptyset(&ss) == 0);
|
||||
pa_assert_se(sigaddset(&ss, p->rtsig) == 0);
|
||||
sigtimedwait(&ss, NULL, &ts);
|
||||
}
|
||||
|
||||
/* And install the new timer */
|
||||
if (p->timer_enabled) {
|
||||
memset(&its, 0, sizeof(its));
|
||||
|
||||
its.it_value.tv_sec = p->next_elapse.tv_sec;
|
||||
its.it_value.tv_nsec = p->next_elapse.tv_usec*1000;
|
||||
|
||||
/* Make sure that 0,0 is not understood as
|
||||
* "disarming" */
|
||||
if (its.it_value.tv_sec == 0)
|
||||
if (its.it_value.tv_sec == 0 && its.it_value.tv_nsec == 0)
|
||||
its.it_value.tv_nsec = 1;
|
||||
|
||||
if (p->period > 0) {
|
||||
struct timeval tv;
|
||||
pa_timeval_store(&tv, p->period);
|
||||
its.it_interval.tv_sec = tv.tv_sec;
|
||||
its.it_interval.tv_nsec = tv.tv_usec*1000;
|
||||
}
|
||||
pa_assert_se(timer_settime(p->timer, TIMER_ABSTIME, &its, NULL) == 0);
|
||||
}
|
||||
|
||||
pa_assert_se(timer_settime(p->timer, TIMER_ABSTIME, &its, NULL) == 0);
|
||||
p->timer_armed = p->timer_enabled;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
|
|
@ -510,23 +502,10 @@ static void update_timer(pa_rtpoll *p) {
|
|||
#endif
|
||||
}
|
||||
|
||||
void pa_rtpoll_set_timer_absolute(pa_rtpoll *p, const struct timeval *ts) {
|
||||
pa_assert(p);
|
||||
pa_assert(ts);
|
||||
|
||||
p->next_elapse = *ts;
|
||||
p->period = 0;
|
||||
p->timer_enabled = TRUE;
|
||||
|
||||
update_timer(p);
|
||||
}
|
||||
|
||||
void pa_rtpoll_set_timer_periodic(pa_rtpoll *p, pa_usec_t usec) {
|
||||
void pa_rtpoll_set_timer_absolute(pa_rtpoll *p, pa_usec_t usec) {
|
||||
pa_assert(p);
|
||||
|
||||
p->period = usec;
|
||||
pa_rtclock_get(&p->next_elapse);
|
||||
pa_timeval_add(&p->next_elapse, usec);
|
||||
pa_timeval_store(&p->next_elapse, usec);
|
||||
p->timer_enabled = TRUE;
|
||||
|
||||
update_timer(p);
|
||||
|
|
@ -535,7 +514,6 @@ void pa_rtpoll_set_timer_periodic(pa_rtpoll *p, pa_usec_t usec) {
|
|||
void pa_rtpoll_set_timer_relative(pa_rtpoll *p, pa_usec_t usec) {
|
||||
pa_assert(p);
|
||||
|
||||
p->period = 0;
|
||||
pa_rtclock_get(&p->next_elapse);
|
||||
pa_timeval_add(&p->next_elapse, usec);
|
||||
p->timer_enabled = TRUE;
|
||||
|
|
@ -546,7 +524,6 @@ void pa_rtpoll_set_timer_relative(pa_rtpoll *p, pa_usec_t usec) {
|
|||
void pa_rtpoll_set_timer_disabled(pa_rtpoll *p) {
|
||||
pa_assert(p);
|
||||
|
||||
p->period = 0;
|
||||
memset(&p->next_elapse, 0, sizeof(p->next_elapse));
|
||||
p->timer_enabled = FALSE;
|
||||
|
||||
|
|
@ -683,23 +660,23 @@ pa_rtpoll_item *pa_rtpoll_item_new_fdsem(pa_rtpoll *p, pa_rtpoll_priority_t prio
|
|||
return i;
|
||||
}
|
||||
|
||||
static int asyncmsgq_before(pa_rtpoll_item *i) {
|
||||
static int asyncmsgq_read_before(pa_rtpoll_item *i) {
|
||||
pa_assert(i);
|
||||
|
||||
if (pa_asyncmsgq_before_poll(i->userdata) < 0)
|
||||
if (pa_asyncmsgq_read_before_poll(i->userdata) < 0)
|
||||
return 1; /* 1 means immediate restart of the loop */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void asyncmsgq_after(pa_rtpoll_item *i) {
|
||||
static void asyncmsgq_read_after(pa_rtpoll_item *i) {
|
||||
pa_assert(i);
|
||||
|
||||
pa_assert((i->pollfd[0].revents & ~POLLIN) == 0);
|
||||
pa_asyncmsgq_after_poll(i->userdata);
|
||||
pa_asyncmsgq_read_after_poll(i->userdata);
|
||||
}
|
||||
|
||||
static int asyncmsgq_work(pa_rtpoll_item *i) {
|
||||
static int asyncmsgq_read_work(pa_rtpoll_item *i) {
|
||||
pa_msgobject *object;
|
||||
int code;
|
||||
void *data;
|
||||
|
|
@ -725,7 +702,7 @@ static int asyncmsgq_work(pa_rtpoll_item *i) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_asyncmsgq *q) {
|
||||
pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq_read(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_asyncmsgq *q) {
|
||||
pa_rtpoll_item *i;
|
||||
struct pollfd *pollfd;
|
||||
|
||||
|
|
@ -735,12 +712,47 @@ pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq(pa_rtpoll *p, pa_rtpoll_priority_t
|
|||
i = pa_rtpoll_item_new(p, prio, 1);
|
||||
|
||||
pollfd = pa_rtpoll_item_get_pollfd(i, NULL);
|
||||
pollfd->fd = pa_asyncmsgq_get_fd(q);
|
||||
pollfd->fd = pa_asyncmsgq_read_fd(q);
|
||||
pollfd->events = POLLIN;
|
||||
|
||||
i->before_cb = asyncmsgq_before;
|
||||
i->after_cb = asyncmsgq_after;
|
||||
i->work_cb = asyncmsgq_work;
|
||||
i->before_cb = asyncmsgq_read_before;
|
||||
i->after_cb = asyncmsgq_read_after;
|
||||
i->work_cb = asyncmsgq_read_work;
|
||||
i->userdata = q;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static int asyncmsgq_write_before(pa_rtpoll_item *i) {
|
||||
pa_assert(i);
|
||||
|
||||
pa_asyncmsgq_write_before_poll(i->userdata);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void asyncmsgq_write_after(pa_rtpoll_item *i) {
|
||||
pa_assert(i);
|
||||
|
||||
pa_assert((i->pollfd[0].revents & ~POLLIN) == 0);
|
||||
pa_asyncmsgq_write_after_poll(i->userdata);
|
||||
}
|
||||
|
||||
pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq_write(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_asyncmsgq *q) {
|
||||
pa_rtpoll_item *i;
|
||||
struct pollfd *pollfd;
|
||||
|
||||
pa_assert(p);
|
||||
pa_assert(q);
|
||||
|
||||
i = pa_rtpoll_item_new(p, prio, 1);
|
||||
|
||||
pollfd = pa_rtpoll_item_get_pollfd(i, NULL);
|
||||
pollfd->fd = pa_asyncmsgq_write_fd(q);
|
||||
pollfd->events = POLLIN;
|
||||
|
||||
i->before_cb = asyncmsgq_write_before;
|
||||
i->after_cb = asyncmsgq_write_after;
|
||||
i->work_cb = NULL;
|
||||
i->userdata = q;
|
||||
|
||||
return i;
|
||||
|
|
|
|||
|
|
@ -74,8 +74,7 @@ void pa_rtpoll_install(pa_rtpoll *p);
|
|||
* cleanly. */
|
||||
int pa_rtpoll_run(pa_rtpoll *f, pa_bool_t wait);
|
||||
|
||||
void pa_rtpoll_set_timer_absolute(pa_rtpoll *p, const struct timeval *ts);
|
||||
void pa_rtpoll_set_timer_periodic(pa_rtpoll *p, pa_usec_t usec);
|
||||
void pa_rtpoll_set_timer_absolute(pa_rtpoll *p, pa_usec_t usec);
|
||||
void pa_rtpoll_set_timer_relative(pa_rtpoll *p, pa_usec_t usec);
|
||||
void pa_rtpoll_set_timer_disabled(pa_rtpoll *p);
|
||||
|
||||
|
|
@ -107,7 +106,8 @@ void pa_rtpoll_item_set_userdata(pa_rtpoll_item *i, void *userdata);
|
|||
void* pa_rtpoll_item_get_userdata(pa_rtpoll_item *i);
|
||||
|
||||
pa_rtpoll_item *pa_rtpoll_item_new_fdsem(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_fdsem *s);
|
||||
pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_asyncmsgq *q);
|
||||
pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq_read(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_asyncmsgq *q);
|
||||
pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq_write(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_asyncmsgq *q);
|
||||
|
||||
/* Requests the loop to exit. Will cause the next iteration of
|
||||
* pa_rtpoll_run() to return 0 */
|
||||
|
|
|
|||
|
|
@ -42,29 +42,6 @@
|
|||
|
||||
#define PA_SILENCE_MAX (PA_PAGE_SIZE*16)
|
||||
|
||||
pa_memblock *pa_silence_memblock_new(pa_mempool *pool, const pa_sample_spec *spec, size_t length) {
|
||||
size_t fs;
|
||||
pa_assert(pool);
|
||||
pa_assert(spec);
|
||||
|
||||
if (length <= 0)
|
||||
length = pa_bytes_per_second(spec)/20; /* 50 ms */
|
||||
|
||||
if (length > PA_SILENCE_MAX)
|
||||
length = PA_SILENCE_MAX;
|
||||
|
||||
fs = pa_frame_size(spec);
|
||||
|
||||
length = (length+fs-1)/fs;
|
||||
|
||||
if (length <= 0)
|
||||
length = 1;
|
||||
|
||||
length *= fs;
|
||||
|
||||
return pa_silence_memblock(pa_memblock_new(pool, length), spec);
|
||||
}
|
||||
|
||||
pa_memblock *pa_silence_memblock(pa_memblock* b, const pa_sample_spec *spec) {
|
||||
void *data;
|
||||
|
||||
|
|
@ -74,10 +51,11 @@ pa_memblock *pa_silence_memblock(pa_memblock* b, const pa_sample_spec *spec) {
|
|||
data = pa_memblock_acquire(b);
|
||||
pa_silence_memory(data, pa_memblock_get_length(b), spec);
|
||||
pa_memblock_release(b);
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
void pa_silence_memchunk(pa_memchunk *c, const pa_sample_spec *spec) {
|
||||
pa_memchunk* pa_silence_memchunk(pa_memchunk *c, const pa_sample_spec *spec) {
|
||||
void *data;
|
||||
|
||||
pa_assert(c);
|
||||
|
|
@ -87,37 +65,38 @@ void pa_silence_memchunk(pa_memchunk *c, const pa_sample_spec *spec) {
|
|||
data = pa_memblock_acquire(c->memblock);
|
||||
pa_silence_memory((uint8_t*) data+c->index, c->length, spec);
|
||||
pa_memblock_release(c->memblock);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
void pa_silence_memory(void *p, size_t length, const pa_sample_spec *spec) {
|
||||
uint8_t c = 0;
|
||||
pa_assert(p);
|
||||
pa_assert(length > 0);
|
||||
pa_assert(spec);
|
||||
|
||||
switch (spec->format) {
|
||||
static uint8_t silence_byte(pa_sample_format_t format) {
|
||||
switch (format) {
|
||||
case PA_SAMPLE_U8:
|
||||
c = 0x80;
|
||||
break;
|
||||
return 0x80;
|
||||
case PA_SAMPLE_S16LE:
|
||||
case PA_SAMPLE_S16BE:
|
||||
case PA_SAMPLE_S32LE:
|
||||
case PA_SAMPLE_S32BE:
|
||||
case PA_SAMPLE_FLOAT32:
|
||||
case PA_SAMPLE_FLOAT32RE:
|
||||
c = 0;
|
||||
break;
|
||||
case PA_SAMPLE_FLOAT32LE:
|
||||
case PA_SAMPLE_FLOAT32BE:
|
||||
return 0;
|
||||
case PA_SAMPLE_ALAW:
|
||||
c = 0xd5;
|
||||
break;
|
||||
return 0xd5;
|
||||
case PA_SAMPLE_ULAW:
|
||||
c = 0xff;
|
||||
break;
|
||||
return 0xff;
|
||||
default:
|
||||
pa_assert_not_reached();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset(p, c, length);
|
||||
void* pa_silence_memory(void *p, size_t length, const pa_sample_spec *spec) {
|
||||
pa_assert(p);
|
||||
pa_assert(length > 0);
|
||||
pa_assert(spec);
|
||||
|
||||
memset(p, silence_byte(spec->format), length);
|
||||
return p;
|
||||
}
|
||||
|
||||
static void calc_linear_integer_stream_volumes(pa_mix_info streams[], unsigned nstreams, const pa_sample_spec *spec) {
|
||||
|
|
@ -631,6 +610,9 @@ void pa_volume_memchunk(
|
|||
pa_assert(c->length % pa_frame_size(spec) == 0);
|
||||
pa_assert(volume);
|
||||
|
||||
if (pa_memblock_is_silence(c->memblock))
|
||||
return;
|
||||
|
||||
if (pa_cvolume_channels_equal_to(volume, PA_VOLUME_NORM))
|
||||
return;
|
||||
|
||||
|
|
@ -931,3 +913,117 @@ void pa_deinterleave(const void *src, void *dst[], unsigned channels, size_t ss,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
static pa_memblock *silence_memblock_new(pa_mempool *pool, uint8_t c) {
|
||||
pa_memblock *b;
|
||||
size_t length;
|
||||
void *data;
|
||||
|
||||
pa_assert(pool);
|
||||
|
||||
length = PA_MIN(pa_mempool_block_size_max(pool), PA_SILENCE_MAX);
|
||||
|
||||
b = pa_memblock_new(pool, length);
|
||||
|
||||
data = pa_memblock_acquire(b);
|
||||
memset(data, c, length);
|
||||
pa_memblock_release(b);
|
||||
|
||||
pa_memblock_set_is_silence(b, TRUE);
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
void pa_silence_cache_init(pa_silence_cache *cache) {
|
||||
pa_assert(cache);
|
||||
|
||||
memset(cache, 0, sizeof(pa_silence_cache));
|
||||
}
|
||||
|
||||
void pa_silence_cache_done(pa_silence_cache *cache) {
|
||||
pa_sample_format_t f;
|
||||
pa_assert(cache);
|
||||
|
||||
for (f = 0; f < PA_SAMPLE_MAX; f++)
|
||||
if (cache->blocks[f])
|
||||
pa_memblock_unref(cache->blocks[f]);
|
||||
|
||||
memset(cache, 0, sizeof(pa_silence_cache));
|
||||
}
|
||||
|
||||
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_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_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_sample_clamp(pa_sample_format_t format, void *dst, size_t dstr, const void *src, size_t sstr, unsigned n) {
|
||||
const float *s;
|
||||
float *d;
|
||||
|
||||
s = src; d = dst;
|
||||
|
||||
if (format == PA_SAMPLE_FLOAT32NE) {
|
||||
|
||||
float minus_one = -1.0, plus_one = 1.0;
|
||||
oil_clip_f32(d, dstr, s, sstr, n, &minus_one, &plus_one);
|
||||
|
||||
} else {
|
||||
pa_assert(format == PA_SAMPLE_FLOAT32RE);
|
||||
|
||||
for (; n > 0; n--) {
|
||||
float f;
|
||||
|
||||
f = PA_FLOAT32_SWAP(*s);
|
||||
f = PA_CLAMP_UNLIKELY(f, -1.0, 1.0);
|
||||
*d = PA_FLOAT32_SWAP(f);
|
||||
|
||||
s = (const float*) ((const uint8_t*) s + sstr);
|
||||
d = (float*) ((uint8_t*) d + dstr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,10 +30,18 @@
|
|||
#include <pulsecore/memblock.h>
|
||||
#include <pulsecore/memchunk.h>
|
||||
|
||||
pa_memblock *pa_silence_memblock(pa_memblock* b, const pa_sample_spec *spec);
|
||||
pa_memblock *pa_silence_memblock_new(pa_mempool *pool, const pa_sample_spec *spec, size_t length);
|
||||
void pa_silence_memchunk(pa_memchunk *c, const pa_sample_spec *spec);
|
||||
void pa_silence_memory(void *p, size_t length, const pa_sample_spec *spec);
|
||||
typedef struct pa_silence_cache {
|
||||
pa_memblock* blocks[PA_SAMPLE_MAX];
|
||||
} pa_silence_cache;
|
||||
|
||||
void pa_silence_cache_init(pa_silence_cache *cache);
|
||||
void pa_silence_cache_done(pa_silence_cache *cache);
|
||||
|
||||
void *pa_silence_memory(void *p, size_t length, const pa_sample_spec *spec);
|
||||
pa_memchunk* pa_silence_memchunk(pa_memchunk *c, const pa_sample_spec *spec);
|
||||
pa_memblock* pa_silence_memblock(pa_memblock *b, const pa_sample_spec *spec);
|
||||
|
||||
pa_memchunk* pa_silence_memchunk_get(pa_silence_cache *cache, pa_mempool *pool, pa_memchunk* ret, const pa_sample_spec *spec, size_t length);
|
||||
|
||||
typedef struct pa_mix_info {
|
||||
pa_memchunk chunk;
|
||||
|
|
@ -70,4 +78,6 @@ int pa_frame_aligned(size_t l, const pa_sample_spec *ss) PA_GCC_PURE;
|
|||
void pa_interleave(const void *src[], unsigned channels, void *dst, size_t ss, unsigned n);
|
||||
void pa_deinterleave(const void *src, void *dst[], unsigned channels, size_t ss, unsigned n);
|
||||
|
||||
void pa_sample_clamp(pa_sample_format_t format, void *dst, size_t dstr, const void *src, size_t sstr, unsigned n);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@
|
|||
#endif
|
||||
|
||||
#include <pulse/xmalloc.h>
|
||||
#include <pulse/gccmacro.h>
|
||||
|
||||
#include <pulsecore/core-error.h>
|
||||
#include <pulsecore/log.h>
|
||||
|
|
@ -56,7 +57,7 @@
|
|||
#define MADV_REMOVE 9
|
||||
#endif
|
||||
|
||||
#define MAX_SHM_SIZE (PA_ALIGN(1024*1024*20))
|
||||
#define MAX_SHM_SIZE (PA_ALIGN(1024*1024*64))
|
||||
|
||||
#ifdef __linux__
|
||||
/* On Linux we know that the shared memory blocks are files in
|
||||
|
|
@ -69,14 +70,15 @@
|
|||
|
||||
#define SHM_MARKER ((int) 0xbeefcafe)
|
||||
|
||||
/* We now put this SHM marker at the end of each segment. It's optional to not require a reboot when upgrading, though */
|
||||
struct shm_marker {
|
||||
/* We now put this SHM marker at the end of each segment. It's
|
||||
* optional, to not require a reboot when upgrading, though */
|
||||
struct shm_marker PA_GCC_PACKED {
|
||||
pa_atomic_t marker; /* 0xbeefcafe */
|
||||
pa_atomic_t pid;
|
||||
void *_reserverd1;
|
||||
void *_reserverd2;
|
||||
void *_reserverd3;
|
||||
void *_reserverd4;
|
||||
uint64_t *_reserverd1;
|
||||
uint64_t *_reserverd2;
|
||||
uint64_t *_reserverd3;
|
||||
uint64_t *_reserverd4;
|
||||
};
|
||||
|
||||
static char *segment_name(char *fn, size_t l, unsigned id) {
|
||||
|
|
@ -84,13 +86,13 @@ static char *segment_name(char *fn, size_t l, unsigned id) {
|
|||
return fn;
|
||||
}
|
||||
|
||||
int pa_shm_create_rw(pa_shm *m, size_t size, int shared, mode_t mode) {
|
||||
int pa_shm_create_rw(pa_shm *m, size_t size, pa_bool_t shared, mode_t mode) {
|
||||
char fn[32];
|
||||
int fd = -1;
|
||||
|
||||
pa_assert(m);
|
||||
pa_assert(size > 0);
|
||||
pa_assert(size < MAX_SHM_SIZE);
|
||||
pa_assert(size <= MAX_SHM_SIZE);
|
||||
pa_assert(mode >= 0600);
|
||||
|
||||
/* Each time we create a new SHM area, let's first drop all stale
|
||||
|
|
@ -122,7 +124,7 @@ int pa_shm_create_rw(pa_shm *m, size_t size, int shared, mode_t mode) {
|
|||
m->ptr = pa_xmalloc(m->size);
|
||||
#endif
|
||||
|
||||
m->do_unlink = 0;
|
||||
m->do_unlink = FALSE;
|
||||
|
||||
} else {
|
||||
#ifdef HAVE_SHM_OPEN
|
||||
|
|
@ -155,7 +157,7 @@ int pa_shm_create_rw(pa_shm *m, size_t size, int shared, mode_t mode) {
|
|||
pa_atomic_store(&marker->marker, SHM_MARKER);
|
||||
|
||||
pa_assert_se(close(fd) == 0);
|
||||
m->do_unlink = 1;
|
||||
m->do_unlink = TRUE;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
|
|
@ -282,7 +284,9 @@ int pa_shm_attach_ro(pa_shm *m, unsigned id) {
|
|||
goto fail;
|
||||
}
|
||||
|
||||
if (st.st_size <= 0 || st.st_size > MAX_SHM_SIZE+PA_ALIGN(sizeof(struct shm_marker)) || PA_ALIGN(st.st_size) != st.st_size) {
|
||||
if (st.st_size <= 0 ||
|
||||
st.st_size > (off_t) (MAX_SHM_SIZE+PA_ALIGN(sizeof(struct shm_marker))) ||
|
||||
PA_ALIGN((size_t) st.st_size) != (size_t) st.st_size) {
|
||||
pa_log("Invalid shared memory segment size");
|
||||
goto fail;
|
||||
}
|
||||
|
|
@ -371,7 +375,7 @@ int pa_shm_cleanup(void) {
|
|||
/* Ok, the owner of this shms segment is dead, so, let's remove the segment */
|
||||
segment_name(fn, sizeof(fn), id);
|
||||
|
||||
if (shm_unlink(fn) < 0 && errno != EACCES)
|
||||
if (shm_unlink(fn) < 0 && errno != EACCES && errno != ENOENT)
|
||||
pa_log_warn("Failed to remove SHM segment %s: %s\n", fn, pa_cstrerror(errno));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,15 +26,17 @@
|
|||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <pulsecore/macro.h>
|
||||
|
||||
typedef struct pa_shm {
|
||||
unsigned id;
|
||||
void *ptr;
|
||||
size_t size;
|
||||
int do_unlink;
|
||||
int shared;
|
||||
pa_bool_t do_unlink:1;
|
||||
pa_bool_t shared:1;
|
||||
} pa_shm;
|
||||
|
||||
int pa_shm_create_rw(pa_shm *m, size_t size, int shared, mode_t mode);
|
||||
int pa_shm_create_rw(pa_shm *m, size_t size, pa_bool_t shared, mode_t mode);
|
||||
int pa_shm_attach_ro(pa_shm *m, unsigned id);
|
||||
|
||||
void pa_shm_punch(pa_shm *m, size_t offset, size_t size);
|
||||
|
|
|
|||
222
src/pulsecore/shmasyncq.c
Normal file
222
src/pulsecore/shmasyncq.c
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
/* $Id$ */
|
||||
|
||||
/***
|
||||
This file is part of PulseAudio.
|
||||
|
||||
Copyright 2006 Lennart Poettering
|
||||
|
||||
PulseAudio is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as
|
||||
published by the Free Software Foundation; either version 2.1 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
PulseAudio is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with PulseAudio; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
USA.
|
||||
***/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <pulsecore/atomic.h>
|
||||
#include <pulsecore/log.h>
|
||||
#include <pulsecore/thread.h>
|
||||
#include <pulsecore/macro.h>
|
||||
#include <pulsecore/core-util.h>
|
||||
#include <pulse/xmalloc.h>
|
||||
|
||||
#include "fdsem.h"
|
||||
|
||||
/* For debugging purposes we can define _Y to put and extra thread
|
||||
* yield between each operation. */
|
||||
|
||||
/* #define PROFILE */
|
||||
|
||||
#ifdef PROFILE
|
||||
#define _Y pa_thread_yield()
|
||||
#else
|
||||
#define _Y do { } while(0)
|
||||
#endif
|
||||
|
||||
|
||||
struct pa_shmasyncq {
|
||||
pa_fdsem *read_fdsem, *write_fdsem;
|
||||
pa_shmasyncq_data *data;
|
||||
};
|
||||
|
||||
static int is_power_of_two(unsigned size) {
|
||||
return !(size & (size - 1));
|
||||
}
|
||||
|
||||
static int reduce(pa_shmasyncq *l, int value) {
|
||||
return value & (unsigned) (l->n_elements - 1);
|
||||
}
|
||||
|
||||
static pa_atomic_t* get_cell(pa_shmasyncq *l, unsigned i) {
|
||||
pa_assert(i < l->data->n_elements);
|
||||
|
||||
return (pa_atomic_t*) ((uint8*t) l->data + PA_ALIGN(sizeof(pa_shmasyncq_data)) + i * (PA_ALIGN(sizeof(pa_atomic_t)) + PA_ALIGN(element_size)))
|
||||
}
|
||||
|
||||
static void *get_cell_data(pa_atomic_t *a) {
|
||||
return (uint8_t*) a + PA_ALIGN(sizeof(atomic_t));
|
||||
}
|
||||
|
||||
pa_shmasyncq *pa_shmasyncq_new(unsigned n_elements, size_t element_size, void *data, int fd[2]) {
|
||||
pa_shmasyncq *l;
|
||||
|
||||
pa_assert(n_elements > 0);
|
||||
pa_assert(is_power_of_two(n_elements));
|
||||
pa_assert(element_size > 0);
|
||||
pa_assert(data);
|
||||
pa_assert(fd);
|
||||
|
||||
l = pa_xnew(pa_shmasyncq, 1);
|
||||
|
||||
l->data = data;
|
||||
memset(data, 0, PA_SHMASYNCQ_SIZE(n_elements, element_size));
|
||||
|
||||
l->data->n_elements = n_elements;
|
||||
l->data->element_size = element_size;
|
||||
|
||||
if (!(l->read_fdsem = pa_fdsem_new_shm(&d->read_fdsem_data, &fd[0]))) {
|
||||
pa_xfree(l);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(l->write_fdsem = pa_fdsem_new(&d->write_fdsem_data, &fd[1]))) {
|
||||
pa_fdsem_free(l->read_fdsem);
|
||||
pa_xfree(l);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
void pa_shmasyncq_free(pa_shmasyncq *l, pa_free_cb_t free_cb) {
|
||||
pa_assert(l);
|
||||
|
||||
if (free_cb) {
|
||||
void *p;
|
||||
|
||||
while ((p = pa_shmasyncq_pop(l, 0)))
|
||||
free_cb(p);
|
||||
}
|
||||
|
||||
pa_fdsem_free(l->read_fdsem);
|
||||
pa_fdsem_free(l->write_fdsem);
|
||||
pa_xfree(l);
|
||||
}
|
||||
|
||||
int pa_shmasyncq_push(pa_shmasyncq*l, void *p, int wait) {
|
||||
int idx;
|
||||
pa_atomic_ptr_t *cells;
|
||||
|
||||
pa_assert(l);
|
||||
pa_assert(p);
|
||||
|
||||
cells = PA_SHMASYNCQ_CELLS(l);
|
||||
|
||||
_Y;
|
||||
idx = reduce(l, l->write_idx);
|
||||
|
||||
if (!pa_atomic_ptr_cmpxchg(&cells[idx], NULL, p)) {
|
||||
|
||||
if (!wait)
|
||||
return -1;
|
||||
|
||||
/* pa_log("sleeping on push"); */
|
||||
|
||||
do {
|
||||
pa_fdsem_wait(l->read_fdsem);
|
||||
} while (!pa_atomic_ptr_cmpxchg(&cells[idx], NULL, p));
|
||||
}
|
||||
|
||||
_Y;
|
||||
l->write_idx++;
|
||||
|
||||
pa_fdsem_post(l->write_fdsem);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* pa_shmasyncq_pop(pa_shmasyncq*l, int wait) {
|
||||
int idx;
|
||||
void *ret;
|
||||
pa_atomic_ptr_t *cells;
|
||||
|
||||
pa_assert(l);
|
||||
|
||||
cells = PA_SHMASYNCQ_CELLS(l);
|
||||
|
||||
_Y;
|
||||
idx = reduce(l, l->read_idx);
|
||||
|
||||
if (!(ret = pa_atomic_ptr_load(&cells[idx]))) {
|
||||
|
||||
if (!wait)
|
||||
return NULL;
|
||||
|
||||
/* pa_log("sleeping on pop"); */
|
||||
|
||||
do {
|
||||
pa_fdsem_wait(l->write_fdsem);
|
||||
} while (!(ret = pa_atomic_ptr_load(&cells[idx])));
|
||||
}
|
||||
|
||||
pa_assert(ret);
|
||||
|
||||
/* Guaranteed to succeed if we only have a single reader */
|
||||
pa_assert_se(pa_atomic_ptr_cmpxchg(&cells[idx], ret, NULL));
|
||||
|
||||
_Y;
|
||||
l->read_idx++;
|
||||
|
||||
pa_fdsem_post(l->read_fdsem);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int pa_shmasyncq_get_fd(pa_shmasyncq *q) {
|
||||
pa_assert(q);
|
||||
|
||||
return pa_fdsem_get(q->write_fdsem);
|
||||
}
|
||||
|
||||
int pa_shmasyncq_before_poll(pa_shmasyncq *l) {
|
||||
int idx;
|
||||
pa_atomic_ptr_t *cells;
|
||||
|
||||
pa_assert(l);
|
||||
|
||||
cells = PA_SHMASYNCQ_CELLS(l);
|
||||
|
||||
_Y;
|
||||
idx = reduce(l, l->read_idx);
|
||||
|
||||
for (;;) {
|
||||
if (pa_atomic_ptr_load(&cells[idx]))
|
||||
return -1;
|
||||
|
||||
if (pa_fdsem_before_poll(l->write_fdsem) >= 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pa_shmasyncq_after_poll(pa_shmasyncq *l) {
|
||||
pa_assert(l);
|
||||
|
||||
pa_fdsem_after_poll(l->write_fdsem);
|
||||
}
|
||||
62
src/pulsecore/shmasyncq.h
Normal file
62
src/pulsecore/shmasyncq.h
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
#ifndef foopulseshmasyncqhfoo
|
||||
#define foopulseshmasyncqhfoo
|
||||
|
||||
/* $Id$ */
|
||||
|
||||
/***
|
||||
This file is part of PulseAudio.
|
||||
|
||||
Copyright 2004-2006 Lennart Poettering
|
||||
|
||||
PulseAudio is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as
|
||||
published by the Free Software Foundation; either version 2.1 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
PulseAudio is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with PulseAudio; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
USA.
|
||||
***/
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <pulse/def.h>
|
||||
#include <pulsecore/macro.h>
|
||||
|
||||
/* Similar to pa_asyncq, but stores data in a shared memory segment */
|
||||
|
||||
|
||||
struct pa_shmasyncq_data {
|
||||
unsigned n_elements;
|
||||
size_t element_size;
|
||||
unsigned read_idx;
|
||||
unsigned write_idx;
|
||||
pa_fdsem_data read_fdsem_data, write_fdsem_data;
|
||||
};
|
||||
|
||||
#define PA_SHMASYNCQ_DEFAULT_N_ELEMENTS 128
|
||||
#define PA_SHMASYNCQ_SIZE(n_elements, element_size) (PA_ALIGN(sizeof(pa_shmasyncq_data)) + (((n_elements) * (PA_ALIGN(sizeof(pa_atomic_t)) + PA_ALIGN(element_size)))))
|
||||
#define PA_SHMASYNCQ_DEFAULT_SIZE(element_size) PA_SHMASYNCQ_SIZE(PA_SHMASYNCQ_DEFAULT_N_ELEMENTS, element_size)
|
||||
|
||||
typedef struct pa_shmasyncq pa_shmasyncq;
|
||||
|
||||
pa_shmasyncq *pa_shmasyncq_new(unsigned n_elements, size_t element_size, void *data, int fd[2]);
|
||||
void pa_shmasyncq_free(pa_shmasyncq* q, pa_free_cb_t free_cb);
|
||||
|
||||
void* pa_shmasyncq_pop_begin(pa_shmasyncq *q, pa_bool_t wait);
|
||||
void pa_shmasyncq_pop_commit(pa_shmasyncq *q);
|
||||
|
||||
int* pa_shmasyncq_push_begin(pa_shmasyncq *q, pa_bool_t wait);
|
||||
void pa_shmasyncq_push_commit(pa_shmasyncq *q);
|
||||
|
||||
int pa_shmasyncq_get_fd(pa_shmasyncq *q);
|
||||
int pa_shmasyncq_before_poll(pa_shmasyncq *a);
|
||||
void pa_shmasyncq_after_poll(pa_shmasyncq *a);
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -46,7 +46,7 @@ typedef enum pa_sink_input_state {
|
|||
PA_SINK_INPUT_UNLINKED /*< The stream is dead */
|
||||
} pa_sink_input_state_t;
|
||||
|
||||
static inline pa_bool_t PA_SINK_INPUT_LINKED(pa_sink_input_state_t x) {
|
||||
static inline pa_bool_t PA_SINK_INPUT_IS_LINKED(pa_sink_input_state_t x) {
|
||||
return x == PA_SINK_INPUT_DRAINED || x == PA_SINK_INPUT_RUNNING || x == PA_SINK_INPUT_CORKED;
|
||||
}
|
||||
|
||||
|
|
@ -73,7 +73,8 @@ struct pa_sink_input {
|
|||
pa_sink_input_state_t state;
|
||||
pa_sink_input_flags_t flags;
|
||||
|
||||
char *name, *driver; /* may be NULL */
|
||||
pa_proplist *proplist;
|
||||
char *driver; /* may be NULL */
|
||||
pa_module *module; /* may be NULL */
|
||||
pa_client *client; /* may be NULL */
|
||||
|
||||
|
|
@ -87,17 +88,26 @@ struct pa_sink_input {
|
|||
pa_cvolume volume;
|
||||
pa_bool_t muted;
|
||||
|
||||
/* Returns the chunk of audio data (but doesn't drop it
|
||||
* yet!). Returns -1 on failure. Called from IO thread context. If
|
||||
* data needs to be generated from scratch then please in the
|
||||
* specified length. This is an optimization only. If less data is
|
||||
* available, it's fine to return a smaller block. If more data is
|
||||
* already ready, it is better to return the full block.*/
|
||||
int (*peek) (pa_sink_input *i, size_t length, pa_memchunk *chunk);
|
||||
pa_resample_method_t resample_method;
|
||||
|
||||
/* Drops the specified number of bytes, usually called right after
|
||||
* peek(), but not necessarily. Called from IO thread context. */
|
||||
void (*drop) (pa_sink_input *i, size_t length);
|
||||
/* Returns the chunk of audio data and drops it from the
|
||||
* queue. Returns -1 on failure. Called from IO thread context. If
|
||||
* data needs to be generated from scratch then please in the
|
||||
* specified length request_nbytes. This is an optimization
|
||||
* only. If less data is available, it's fine to return a smaller
|
||||
* block. If more data is already ready, it is better to return
|
||||
* the full block. */
|
||||
int (*pop) (pa_sink_input *i, size_t request_nbytes, pa_memchunk *chunk); /* may NOT be NULL */
|
||||
|
||||
/* Rewind the queue by the specified number of bytes. Called just
|
||||
* before peek() if it is called at all. Only called if the sink
|
||||
* input driver ever plans to call
|
||||
* pa_sink_input_request_rewind(). Called from IO context. */
|
||||
void (*process_rewind) (pa_sink_input *i, size_t nbytes); /* may NOT be NULL */
|
||||
|
||||
/* Called whenever the maximum rewindable size of the sink
|
||||
* changes. Called from IO context. */
|
||||
void (*update_max_rewind) (pa_sink_input *i, size_t nbytes); /* may be NULL */
|
||||
|
||||
/* If non-NULL this function is called when the input is first
|
||||
* connected to a sink or when the rtpoll/asyncmsgq fields
|
||||
|
|
@ -128,7 +138,9 @@ struct pa_sink_input {
|
|||
instead. */
|
||||
pa_usec_t (*get_latency) (pa_sink_input *i); /* may be NULL */
|
||||
|
||||
pa_resample_method_t resample_method;
|
||||
/* If non_NULL this function is called from thread context if the
|
||||
* state changes. The old state is found in thread_info.state. */
|
||||
void (*state_change) (pa_sink_input *i, pa_sink_input_state_t state); /* may be NULL */
|
||||
|
||||
struct {
|
||||
pa_sink_input_state_t state;
|
||||
|
|
@ -138,19 +150,22 @@ struct pa_sink_input {
|
|||
|
||||
pa_sample_spec sample_spec;
|
||||
|
||||
pa_memchunk resampled_chunk;
|
||||
pa_resampler *resampler; /* may be NULL */
|
||||
|
||||
/* Some silence to play before the actual data. This is used to
|
||||
* compensate for latency differences when moving a sink input
|
||||
* "hot" between sinks. */
|
||||
size_t move_silence;
|
||||
pa_memblock *silence_memblock; /* may be NULL */
|
||||
/* We maintain a history of resampled audio data here. */
|
||||
pa_memblockq *render_memblockq;
|
||||
|
||||
size_t rewrite_nbytes;
|
||||
pa_bool_t rewrite_flush;
|
||||
uint64_t underrun_for, playing_for;
|
||||
|
||||
pa_sink_input *sync_prev, *sync_next;
|
||||
|
||||
pa_cvolume volume;
|
||||
pa_bool_t muted;
|
||||
|
||||
/* The requested latency for the sink */
|
||||
pa_usec_t requested_sink_latency;
|
||||
} thread_info;
|
||||
|
||||
void *userdata;
|
||||
|
|
@ -165,11 +180,15 @@ enum {
|
|||
PA_SINK_INPUT_MESSAGE_GET_LATENCY,
|
||||
PA_SINK_INPUT_MESSAGE_SET_RATE,
|
||||
PA_SINK_INPUT_MESSAGE_SET_STATE,
|
||||
PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY,
|
||||
PA_SINK_INPUT_MESSAGE_GET_REQUESTED_LATENCY,
|
||||
PA_SINK_INPUT_MESSAGE_MAX
|
||||
};
|
||||
|
||||
typedef struct pa_sink_input_new_data {
|
||||
const char *name, *driver;
|
||||
pa_proplist *proplist;
|
||||
|
||||
const char *driver;
|
||||
pa_module *module;
|
||||
pa_client *client;
|
||||
|
||||
|
|
@ -190,16 +209,17 @@ typedef struct pa_sink_input_new_data {
|
|||
pa_sink_input *sync_base;
|
||||
} pa_sink_input_new_data;
|
||||
|
||||
typedef struct pa_sink_input_move_hook_data {
|
||||
pa_sink_input *sink_input;
|
||||
pa_sink *destination;
|
||||
} pa_sink_input_move_hook_data;
|
||||
|
||||
pa_sink_input_new_data* pa_sink_input_new_data_init(pa_sink_input_new_data *data);
|
||||
void pa_sink_input_new_data_set_sample_spec(pa_sink_input_new_data *data, const pa_sample_spec *spec);
|
||||
void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const pa_channel_map *map);
|
||||
void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume);
|
||||
void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute);
|
||||
void pa_sink_input_new_data_done(pa_sink_input_new_data *data);
|
||||
|
||||
typedef struct pa_sink_input_move_hook_data {
|
||||
pa_sink_input *sink_input;
|
||||
pa_sink *destination;
|
||||
} pa_sink_input_move_hook_data;
|
||||
|
||||
/* To be called by the implementing module only */
|
||||
|
||||
|
|
@ -213,7 +233,22 @@ void pa_sink_input_unlink(pa_sink_input* i);
|
|||
|
||||
void pa_sink_input_set_name(pa_sink_input *i, const char *name);
|
||||
|
||||
/* Callable by everyone */
|
||||
pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec);
|
||||
|
||||
/* Request that the specified number of bytes already written out to
|
||||
the hw device is rewritten, if possible. Please note that this is
|
||||
only a kind request. The sink driver may not be able to fulfill it
|
||||
fully -- or at all. If the request for a rewrite was successful, the
|
||||
sink driver will call ->rewind() and pass the number of bytes that
|
||||
could be rewound in the HW device. This functionality is required for
|
||||
implementing the "zero latency" write-through functionality. */
|
||||
void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes, pa_bool_t rewrite, pa_bool_t flush);
|
||||
|
||||
void pa_sink_input_cork(pa_sink_input *i, pa_bool_t b);
|
||||
|
||||
int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate);
|
||||
|
||||
/* Callable by everyone from main thread*/
|
||||
|
||||
/* External code may request disconnection with this function */
|
||||
void pa_sink_input_kill(pa_sink_input*i);
|
||||
|
|
@ -225,27 +260,29 @@ const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i);
|
|||
void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute);
|
||||
int pa_sink_input_get_mute(pa_sink_input *i);
|
||||
|
||||
void pa_sink_input_cork(pa_sink_input *i, pa_bool_t b);
|
||||
|
||||
int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate);
|
||||
|
||||
pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i);
|
||||
|
||||
int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately);
|
||||
int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest);
|
||||
|
||||
pa_sink_input_state_t pa_sink_input_get_state(pa_sink_input *i);
|
||||
|
||||
/* To be used exclusively by the sink driver thread */
|
||||
pa_usec_t pa_sink_input_get_requested_latency(pa_sink_input *i);
|
||||
|
||||
/* To be used exclusively by the sink driver IO thread */
|
||||
|
||||
int pa_sink_input_peek(pa_sink_input *i, size_t length, pa_memchunk *chunk, pa_cvolume *volume);
|
||||
void pa_sink_input_drop(pa_sink_input *i, size_t length);
|
||||
void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */);
|
||||
void pa_sink_input_update_max_rewind(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */);
|
||||
|
||||
void pa_sink_input_set_state_within_thread(pa_sink_input *i, pa_sink_input_state_t state);
|
||||
|
||||
int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
|
||||
|
||||
typedef struct pa_sink_input_move_info {
|
||||
pa_sink_input *sink_input;
|
||||
pa_sink_input *ghost_sink_input;
|
||||
pa_memblockq *buffer;
|
||||
size_t buffer_bytes;
|
||||
} pa_sink_input_move_info;
|
||||
pa_usec_t pa_sink_input_set_requested_latency_within_thread(pa_sink_input *i, pa_usec_t usec);
|
||||
|
||||
pa_bool_t pa_sink_input_safe_to_remove(pa_sink_input *i);
|
||||
|
||||
pa_memchunk* pa_sink_input_get_silence(pa_sink_input *i, pa_memchunk *ret);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -33,7 +33,6 @@ typedef struct pa_sink pa_sink;
|
|||
#include <pulse/channelmap.h>
|
||||
#include <pulse/volume.h>
|
||||
|
||||
#include <pulsecore/core-def.h>
|
||||
#include <pulsecore/core.h>
|
||||
#include <pulsecore/idxset.h>
|
||||
#include <pulsecore/source.h>
|
||||
|
|
@ -52,11 +51,11 @@ typedef enum pa_sink_state {
|
|||
PA_SINK_UNLINKED
|
||||
} pa_sink_state_t;
|
||||
|
||||
static inline pa_bool_t PA_SINK_OPENED(pa_sink_state_t x) {
|
||||
static inline pa_bool_t PA_SINK_IS_OPENED(pa_sink_state_t x) {
|
||||
return x == PA_SINK_RUNNING || x == PA_SINK_IDLE;
|
||||
}
|
||||
|
||||
static inline pa_bool_t PA_SINK_LINKED(pa_sink_state_t x) {
|
||||
static inline pa_bool_t PA_SINK_IS_LINKED(pa_sink_state_t x) {
|
||||
return x == PA_SINK_RUNNING || x == PA_SINK_IDLE || x == PA_SINK_SUSPENDED;
|
||||
}
|
||||
|
||||
|
|
@ -69,7 +68,8 @@ struct pa_sink {
|
|||
pa_sink_flags_t flags;
|
||||
|
||||
char *name;
|
||||
char *description, *driver; /* may be NULL */
|
||||
char *driver; /* may be NULL */
|
||||
pa_proplist *proplist;
|
||||
|
||||
pa_module *module; /* may be NULL */
|
||||
|
||||
|
|
@ -85,16 +85,47 @@ struct pa_sink {
|
|||
pa_bool_t refresh_volume;
|
||||
pa_bool_t refresh_mute;
|
||||
|
||||
int (*set_state)(pa_sink *s, pa_sink_state_t state); /* may be NULL */
|
||||
int (*set_volume)(pa_sink *s); /* dito */
|
||||
int (*get_volume)(pa_sink *s); /* dito */
|
||||
int (*get_mute)(pa_sink *s); /* dito */
|
||||
int (*set_mute)(pa_sink *s); /* dito */
|
||||
pa_usec_t (*get_latency)(pa_sink *s); /* dito */
|
||||
|
||||
pa_asyncmsgq *asyncmsgq;
|
||||
pa_rtpoll *rtpoll;
|
||||
|
||||
pa_memchunk silence;
|
||||
|
||||
pa_usec_t min_latency; /* we won't go below this latency */
|
||||
pa_usec_t max_latency; /* An upper limit for the latencies */
|
||||
|
||||
/* Called when the main loop requests a state change. Called from
|
||||
* main loop context. If returns -1 the state change will be
|
||||
* inhibited */
|
||||
int (*set_state)(pa_sink *s, pa_sink_state_t state); /* may be NULL */
|
||||
|
||||
/* Callled when the volume is queried. Called from main loop
|
||||
* context. If this is NULL a PA_SINK_MESSAGE_GET_VOLUME message
|
||||
* will be sent to the IO thread instead. */
|
||||
int (*get_volume)(pa_sink *s); /* may be null */
|
||||
|
||||
/* Called when the volume shall be changed. Called from main loop
|
||||
* context. If this is NULL a PA_SINK_MESSAGE_SET_VOLUME message
|
||||
* will be sent to the IO thread instead. */
|
||||
int (*set_volume)(pa_sink *s); /* dito */
|
||||
|
||||
/* Called when the mute setting is queried. Called from main loop
|
||||
* context. If this is NULL a PA_SINK_MESSAGE_GET_MUTE message
|
||||
* will be sent to the IO thread instead. */
|
||||
int (*get_mute)(pa_sink *s); /* dito */
|
||||
|
||||
/* Called when the mute setting shall be changed. Called from main
|
||||
* loop context. If this is NULL a PA_SINK_MESSAGE_SET_MUTE
|
||||
* message will be sent to the IO thread instead. */
|
||||
int (*set_mute)(pa_sink *s); /* dito */
|
||||
|
||||
/* Called when a rewind request is issued. Called from IO thread
|
||||
* context. */
|
||||
void (*request_rewind)(pa_sink *s); /* dito */
|
||||
|
||||
/* Called when a the requested latency is changed. Called from IO
|
||||
* thread context. */
|
||||
void (*update_requested_latency)(pa_sink *s); /* dito */
|
||||
|
||||
/* Contains copies of the above data so that the real-time worker
|
||||
* thread can work without access locking */
|
||||
struct {
|
||||
|
|
@ -102,9 +133,17 @@ struct pa_sink {
|
|||
pa_hashmap *inputs;
|
||||
pa_cvolume soft_volume;
|
||||
pa_bool_t soft_muted;
|
||||
} thread_info;
|
||||
|
||||
pa_memblock *silence;
|
||||
pa_bool_t requested_latency_valid;
|
||||
pa_usec_t requested_latency;
|
||||
|
||||
/* The number of bytes we need keep around to be able to satisfy
|
||||
* every DMA buffer rewrite */
|
||||
size_t max_rewind;
|
||||
|
||||
/* Maximum of what clients requested to rewind in this cycle */
|
||||
size_t rewind_nbytes;
|
||||
} thread_info;
|
||||
|
||||
void *userdata;
|
||||
};
|
||||
|
|
@ -120,28 +159,52 @@ typedef enum pa_sink_message {
|
|||
PA_SINK_MESSAGE_GET_MUTE,
|
||||
PA_SINK_MESSAGE_SET_MUTE,
|
||||
PA_SINK_MESSAGE_GET_LATENCY,
|
||||
PA_SINK_MESSAGE_GET_REQUESTED_LATENCY,
|
||||
PA_SINK_MESSAGE_SET_STATE,
|
||||
PA_SINK_MESSAGE_PING,
|
||||
PA_SINK_MESSAGE_REMOVE_INPUT_AND_BUFFER,
|
||||
PA_SINK_MESSAGE_START_MOVE,
|
||||
PA_SINK_MESSAGE_FINISH_MOVE,
|
||||
PA_SINK_MESSAGE_ATTACH,
|
||||
PA_SINK_MESSAGE_DETACH,
|
||||
PA_SINK_MESSAGE_MAX
|
||||
} pa_sink_message_t;
|
||||
|
||||
typedef struct pa_sink_new_data {
|
||||
char *name;
|
||||
pa_bool_t namereg_fail;
|
||||
pa_proplist *proplist;
|
||||
|
||||
const char *driver;
|
||||
pa_module *module;
|
||||
|
||||
pa_sample_spec sample_spec;
|
||||
pa_bool_t sample_spec_is_set;
|
||||
pa_channel_map channel_map;
|
||||
pa_bool_t channel_map_is_set;
|
||||
|
||||
pa_cvolume volume;
|
||||
pa_bool_t volume_is_set;
|
||||
pa_bool_t muted;
|
||||
pa_bool_t muted_is_set;
|
||||
} pa_sink_new_data;
|
||||
|
||||
pa_sink_new_data* pa_sink_new_data_init(pa_sink_new_data *data);
|
||||
void pa_sink_new_data_set_name(pa_sink_new_data *data, const char *name);
|
||||
void pa_sink_new_data_set_sample_spec(pa_sink_new_data *data, const pa_sample_spec *spec);
|
||||
void pa_sink_new_data_set_channel_map(pa_sink_new_data *data, const pa_channel_map *map);
|
||||
void pa_sink_new_data_set_volume(pa_sink_new_data *data, const pa_cvolume *volume);
|
||||
void pa_sink_new_data_set_muted(pa_sink_new_data *data, pa_bool_t mute);
|
||||
void pa_sink_new_data_done(pa_sink_new_data *data);
|
||||
|
||||
/* To be called exclusively by the sink driver, from main context */
|
||||
|
||||
pa_sink* pa_sink_new(
|
||||
pa_core *core,
|
||||
const char *driver,
|
||||
const char *name,
|
||||
int namereg_fail,
|
||||
const pa_sample_spec *spec,
|
||||
const pa_channel_map *map);
|
||||
pa_sink_new_data *data,
|
||||
pa_sink_flags_t flags);
|
||||
|
||||
void pa_sink_put(pa_sink *s);
|
||||
void pa_sink_unlink(pa_sink* s);
|
||||
|
||||
void pa_sink_set_module(pa_sink *sink, pa_module *m);
|
||||
void pa_sink_set_description(pa_sink *s, const char *description);
|
||||
void pa_sink_set_asyncmsgq(pa_sink *s, pa_asyncmsgq *q);
|
||||
void pa_sink_set_rtpoll(pa_sink *s, pa_rtpoll *p);
|
||||
|
|
@ -151,17 +214,14 @@ void pa_sink_attach(pa_sink *s);
|
|||
|
||||
/* May be called by everyone, from main context */
|
||||
|
||||
/* The returned value is supposed to be in the time domain of the sound card! */
|
||||
pa_usec_t pa_sink_get_latency(pa_sink *s);
|
||||
pa_usec_t pa_sink_get_requested_latency(pa_sink *s);
|
||||
|
||||
int pa_sink_update_status(pa_sink*s);
|
||||
int pa_sink_suspend(pa_sink *s, pa_bool_t suspend);
|
||||
int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend);
|
||||
|
||||
/* Sends a ping message to the sink thread, to make it wake up and
|
||||
* check for data to process even if there is no real message is
|
||||
* sent */
|
||||
void pa_sink_ping(pa_sink *s);
|
||||
|
||||
void pa_sink_set_volume(pa_sink *sink, const pa_cvolume *volume);
|
||||
const pa_cvolume *pa_sink_get_volume(pa_sink *sink);
|
||||
void pa_sink_set_mute(pa_sink *sink, pa_bool_t mute);
|
||||
|
|
@ -178,11 +238,21 @@ void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result);
|
|||
void pa_sink_render_into(pa_sink*s, pa_memchunk *target);
|
||||
void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target);
|
||||
|
||||
void pa_sink_skip(pa_sink *s, size_t length);
|
||||
void pa_sink_process_rewind(pa_sink *s, size_t nbytes);
|
||||
|
||||
int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
|
||||
|
||||
void pa_sink_attach_within_thread(pa_sink *s);
|
||||
void pa_sink_detach_within_thread(pa_sink *s);
|
||||
|
||||
pa_usec_t pa_sink_get_requested_latency_within_thread(pa_sink *s);
|
||||
|
||||
void pa_sink_set_max_rewind(pa_sink *s, size_t max_rewind);
|
||||
|
||||
/* To be called exclusively by sink input drivers, from IO context */
|
||||
|
||||
void pa_sink_request_rewind(pa_sink*s, size_t nbytes);
|
||||
|
||||
void pa_sink_invalidate_requested_latency(pa_sink *s);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -77,9 +77,9 @@ struct pa_socket_client {
|
|||
pa_io_event *io_event;
|
||||
pa_time_event *timeout_event;
|
||||
pa_defer_event *defer_event;
|
||||
void (*callback)(pa_socket_client*c, pa_iochannel *io, void *userdata);
|
||||
pa_socket_client_cb_t callback;
|
||||
void *userdata;
|
||||
int local;
|
||||
pa_bool_t local;
|
||||
#ifdef HAVE_LIBASYNCNS
|
||||
asyncns_t *asyncns;
|
||||
asyncns_query_t * asyncns_query;
|
||||
|
|
@ -87,7 +87,7 @@ struct pa_socket_client {
|
|||
#endif
|
||||
};
|
||||
|
||||
static pa_socket_client*pa_socket_client_new(pa_mainloop_api *m) {
|
||||
static pa_socket_client* socket_client_new(pa_mainloop_api *m) {
|
||||
pa_socket_client *c;
|
||||
pa_assert(m);
|
||||
|
||||
|
|
@ -96,11 +96,11 @@ static pa_socket_client*pa_socket_client_new(pa_mainloop_api *m) {
|
|||
c->mainloop = m;
|
||||
c->fd = -1;
|
||||
c->io_event = NULL;
|
||||
c->defer_event = NULL;
|
||||
c->timeout_event = NULL;
|
||||
c->defer_event = NULL;
|
||||
c->callback = NULL;
|
||||
c->userdata = NULL;
|
||||
c->local = 0;
|
||||
c->local = FALSE;
|
||||
|
||||
#ifdef HAVE_LIBASYNCNS
|
||||
c->asyncns = NULL;
|
||||
|
|
@ -119,15 +119,15 @@ static void free_events(pa_socket_client *c) {
|
|||
c->io_event = NULL;
|
||||
}
|
||||
|
||||
if (c->defer_event) {
|
||||
c->mainloop->defer_free(c->defer_event);
|
||||
c->defer_event = NULL;
|
||||
}
|
||||
|
||||
if (c->timeout_event) {
|
||||
c->mainloop->time_free(c->timeout_event);
|
||||
c->timeout_event = NULL;
|
||||
}
|
||||
|
||||
if (c->defer_event) {
|
||||
c->mainloop->defer_free(c->defer_event);
|
||||
c->defer_event = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void do_call(pa_socket_client *c) {
|
||||
|
|
@ -177,7 +177,7 @@ finish:
|
|||
pa_socket_client_unref(c);
|
||||
}
|
||||
|
||||
static void connect_fixed_cb(pa_mainloop_api *m, pa_defer_event *e, void *userdata) {
|
||||
static void connect_defer_cb(pa_mainloop_api *m, pa_defer_event *e, void *userdata) {
|
||||
pa_socket_client *c = userdata;
|
||||
|
||||
pa_assert(m);
|
||||
|
|
@ -223,7 +223,7 @@ static int do_connect(pa_socket_client *c, const struct sockaddr *sa, socklen_t
|
|||
|
||||
pa_assert_se(c->io_event = c->mainloop->io_new(c->mainloop, c->fd, PA_IO_EVENT_OUTPUT, connect_io_cb, c));
|
||||
} else
|
||||
pa_assert_se(c->defer_event = c->mainloop->defer_new(c->mainloop, connect_fixed_cb, c));
|
||||
pa_assert_se(c->defer_event = c->mainloop->defer_new(c->mainloop, connect_defer_cb, c));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -252,8 +252,7 @@ pa_socket_client* pa_socket_client_new_unix(pa_mainloop_api *m, const char *file
|
|||
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sun_family = AF_UNIX;
|
||||
strncpy(sa.sun_path, filename, sizeof(sa.sun_path)-1);
|
||||
sa.sun_path[sizeof(sa.sun_path) - 1] = 0;
|
||||
pa_strlcpy(sa.sun_path, filename, sizeof(sa.sun_path));
|
||||
|
||||
return pa_socket_client_new_sockaddr(m, (struct sockaddr*) &sa, sizeof(sa));
|
||||
}
|
||||
|
|
@ -273,7 +272,7 @@ static int sockaddr_prepare(pa_socket_client *c, const struct sockaddr *sa, size
|
|||
|
||||
switch (sa->sa_family) {
|
||||
case AF_UNIX:
|
||||
c->local = 1;
|
||||
c->local = TRUE;
|
||||
break;
|
||||
|
||||
case AF_INET:
|
||||
|
|
@ -285,7 +284,7 @@ static int sockaddr_prepare(pa_socket_client *c, const struct sockaddr *sa, size
|
|||
break;
|
||||
|
||||
default:
|
||||
c->local = 0;
|
||||
c->local = FALSE;
|
||||
}
|
||||
|
||||
if ((c->fd = socket(sa->sa_family, SOCK_STREAM, 0)) < 0) {
|
||||
|
|
@ -294,6 +293,7 @@ static int sockaddr_prepare(pa_socket_client *c, const struct sockaddr *sa, size
|
|||
}
|
||||
|
||||
pa_make_fd_cloexec(c->fd);
|
||||
|
||||
if (sa->sa_family == AF_INET || sa->sa_family == AF_INET6)
|
||||
pa_make_tcp_socket_low_delay(c->fd);
|
||||
else
|
||||
|
|
@ -312,7 +312,7 @@ pa_socket_client* pa_socket_client_new_sockaddr(pa_mainloop_api *m, const struct
|
|||
pa_assert(sa);
|
||||
pa_assert(salen > 0);
|
||||
|
||||
pa_assert_se(c = pa_socket_client_new(m));
|
||||
pa_assert_se(c = socket_client_new(m));
|
||||
|
||||
if (sockaddr_prepare(c, sa, salen) < 0)
|
||||
goto fail;
|
||||
|
|
@ -361,7 +361,7 @@ pa_socket_client* pa_socket_client_ref(pa_socket_client *c) {
|
|||
return c;
|
||||
}
|
||||
|
||||
void pa_socket_client_set_callback(pa_socket_client *c, void (*on_connection)(pa_socket_client *c, pa_iochannel*io, void *userdata), void *userdata) {
|
||||
void pa_socket_client_set_callback(pa_socket_client *c, pa_socket_client_cb_t on_connection, void *userdata) {
|
||||
pa_assert(c);
|
||||
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
||||
|
||||
|
|
@ -489,23 +489,22 @@ pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char*nam
|
|||
hints.ai_family = a.type == PA_PARSED_ADDRESS_TCP4 ? PF_INET : (a.type == PA_PARSED_ADDRESS_TCP6 ? PF_INET6 : PF_UNSPEC);
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
#ifdef HAVE_LIBASYNCNS
|
||||
#if defined(HAVE_LIBASYNCNS)
|
||||
{
|
||||
asyncns_t *asyncns;
|
||||
|
||||
if (!(asyncns = asyncns_new(1)))
|
||||
goto finish;
|
||||
|
||||
c = pa_socket_client_new(m);
|
||||
pa_assert_se(c = socket_client_new(m));
|
||||
c->asyncns = asyncns;
|
||||
c->asyncns_io_event = m->io_new(m, asyncns_fd(c->asyncns), PA_IO_EVENT_INPUT, asyncns_cb, c);
|
||||
c->asyncns_query = asyncns_getaddrinfo(c->asyncns, a.path_or_host, port, &hints);
|
||||
pa_assert(c->asyncns_query);
|
||||
start_timeout(c);
|
||||
}
|
||||
#else /* HAVE_LIBASYNCNS */
|
||||
#elif defined(HAVE_GETADDRINFO)
|
||||
{
|
||||
#ifdef HAVE_GETADDRINFO
|
||||
int ret;
|
||||
struct addrinfo *res = NULL;
|
||||
|
||||
|
|
@ -520,7 +519,9 @@ pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char*nam
|
|||
}
|
||||
|
||||
freeaddrinfo(res);
|
||||
#else /* HAVE_GETADDRINFO */
|
||||
}
|
||||
#else
|
||||
{
|
||||
struct hostent *host = NULL;
|
||||
struct sockaddr_in s;
|
||||
|
||||
|
|
@ -546,7 +547,6 @@ pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char*nam
|
|||
|
||||
if ((c = pa_socket_client_new_sockaddr(m, (struct sockaddr*)&s, sizeof(s))))
|
||||
start_timeout(c);
|
||||
#endif /* HAVE_GETADDRINFO */
|
||||
}
|
||||
#endif /* HAVE_LIBASYNCNS */
|
||||
}
|
||||
|
|
@ -561,7 +561,7 @@ finish:
|
|||
/* Return non-zero when the target sockaddr is considered
|
||||
local. "local" means UNIX socket or TCP socket on localhost. Other
|
||||
local IP addresses are not considered local. */
|
||||
int pa_socket_client_is_local(pa_socket_client *c) {
|
||||
pa_bool_t pa_socket_client_is_local(pa_socket_client *c) {
|
||||
pa_assert(c);
|
||||
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
||||
|
||||
|
|
|
|||
|
|
@ -34,17 +34,19 @@ struct sockaddr;
|
|||
|
||||
typedef struct pa_socket_client pa_socket_client;
|
||||
|
||||
typedef void (*pa_socket_client_cb_t)(pa_socket_client *c, pa_iochannel*io, void *userdata);
|
||||
|
||||
pa_socket_client* pa_socket_client_new_ipv4(pa_mainloop_api *m, uint32_t address, uint16_t port);
|
||||
pa_socket_client* pa_socket_client_new_ipv6(pa_mainloop_api *m, uint8_t address[16], uint16_t port);
|
||||
pa_socket_client* pa_socket_client_new_unix(pa_mainloop_api *m, const char *filename);
|
||||
pa_socket_client* pa_socket_client_new_sockaddr(pa_mainloop_api *m, const struct sockaddr *sa, size_t salen);
|
||||
pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char *a, uint16_t default_port);
|
||||
|
||||
void pa_socket_client_unref(pa_socket_client *c);
|
||||
pa_socket_client* pa_socket_client_ref(pa_socket_client *c);
|
||||
void pa_socket_client_unref(pa_socket_client *c);
|
||||
|
||||
void pa_socket_client_set_callback(pa_socket_client *c, void (*on_connection)(pa_socket_client *c, pa_iochannel*io, void *userdata), void *userdata);
|
||||
void pa_socket_client_set_callback(pa_socket_client *c, pa_socket_client_cb_t on_connection, void *userdata);
|
||||
|
||||
int pa_socket_client_is_local(pa_socket_client *c);
|
||||
pa_bool_t pa_socket_client_is_local(pa_socket_client *c);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
/***
|
||||
This file is part of PulseAudio.
|
||||
|
||||
Copyright 2004-2006 Lennart Poettering
|
||||
Copyright 2004-2008 Lennart Poettering
|
||||
|
||||
PulseAudio is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published
|
||||
|
|
@ -41,17 +41,23 @@
|
|||
#include <pulsecore/log.h>
|
||||
#include <pulsecore/thread-mq.h>
|
||||
#include <pulsecore/core-util.h>
|
||||
#include <pulsecore/sample-util.h>
|
||||
|
||||
#include "sound-file-stream.h"
|
||||
|
||||
#define MEMBLOCKQ_MAXLENGTH (16*1024*1024)
|
||||
|
||||
typedef struct file_stream {
|
||||
pa_msgobject parent;
|
||||
pa_core *core;
|
||||
SNDFILE *sndfile;
|
||||
pa_sink_input *sink_input;
|
||||
pa_memchunk memchunk;
|
||||
|
||||
SNDFILE *sndfile;
|
||||
sf_count_t (*readf_function)(SNDFILE *sndfile, void *ptr, sf_count_t frames);
|
||||
size_t drop;
|
||||
|
||||
/* We need this memblockq here to easily fulfill rewind requests
|
||||
* (even beyond the file start!) */
|
||||
pa_memblockq *memblockq;
|
||||
} file_stream;
|
||||
|
||||
enum {
|
||||
|
|
@ -62,6 +68,7 @@ PA_DECLARE_CLASS(file_stream);
|
|||
#define FILE_STREAM(o) (file_stream_cast(o))
|
||||
static PA_DEFINE_CHECK_TYPE(file_stream, pa_msgobject);
|
||||
|
||||
/* Called from main context */
|
||||
static void file_stream_unlink(file_stream *u) {
|
||||
pa_assert(u);
|
||||
|
||||
|
|
@ -69,7 +76,6 @@ static void file_stream_unlink(file_stream *u) {
|
|||
return;
|
||||
|
||||
pa_sink_input_unlink(u->sink_input);
|
||||
|
||||
pa_sink_input_unref(u->sink_input);
|
||||
u->sink_input = NULL;
|
||||
|
||||
|
|
@ -77,14 +83,13 @@ static void file_stream_unlink(file_stream *u) {
|
|||
file_stream_unref(u);
|
||||
}
|
||||
|
||||
/* Called from main context */
|
||||
static void file_stream_free(pa_object *o) {
|
||||
file_stream *u = FILE_STREAM(o);
|
||||
pa_assert(u);
|
||||
|
||||
file_stream_unlink(u);
|
||||
|
||||
if (u->memchunk.memblock)
|
||||
pa_memblock_unref(u->memchunk.memblock);
|
||||
if (u->memblockq)
|
||||
pa_memblockq_free(u->memblockq);
|
||||
|
||||
if (u->sndfile)
|
||||
sf_close(u->sndfile);
|
||||
|
|
@ -92,6 +97,7 @@ static void file_stream_free(pa_object *o) {
|
|||
pa_xfree(u);
|
||||
}
|
||||
|
||||
/* Called from main context */
|
||||
static int file_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
|
||||
file_stream *u = FILE_STREAM(o);
|
||||
file_stream_assert_ref(u);
|
||||
|
|
@ -105,117 +111,128 @@ static int file_stream_process_msg(pa_msgobject *o, int code, void*userdata, int
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Called from main context */
|
||||
static void sink_input_kill_cb(pa_sink_input *i) {
|
||||
pa_sink_input_assert_ref(i);
|
||||
|
||||
file_stream_unlink(FILE_STREAM(i->userdata));
|
||||
}
|
||||
|
||||
static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
|
||||
file_stream *u;
|
||||
|
||||
pa_assert(i);
|
||||
pa_sink_input_assert_ref(i);
|
||||
u = FILE_STREAM(i->userdata);
|
||||
file_stream_assert_ref(u);
|
||||
|
||||
file_stream_unlink(u);
|
||||
}
|
||||
|
||||
/* Called from IO thread context */
|
||||
static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) {
|
||||
file_stream *u;
|
||||
|
||||
pa_sink_input_assert_ref(i);
|
||||
u = FILE_STREAM(i->userdata);
|
||||
file_stream_assert_ref(u);
|
||||
|
||||
/* If we are added for the first time, ask for a rewinding so that
|
||||
* we are heard right-away. */
|
||||
if (PA_SINK_INPUT_IS_LINKED(state) &&
|
||||
i->thread_info.state == PA_SINK_INPUT_INIT)
|
||||
pa_sink_input_request_rewind(i, 0, FALSE, TRUE);
|
||||
}
|
||||
|
||||
/* Called from IO thread context */
|
||||
static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
|
||||
file_stream *u;
|
||||
|
||||
pa_sink_input_assert_ref(i);
|
||||
pa_assert(chunk);
|
||||
u = FILE_STREAM(i->userdata);
|
||||
file_stream_assert_ref(u);
|
||||
|
||||
if (!u->sndfile)
|
||||
if (!u->memblockq)
|
||||
return -1;
|
||||
|
||||
pa_log_debug("pop: %lu", (unsigned long) length);
|
||||
|
||||
for (;;) {
|
||||
pa_memchunk tchunk;
|
||||
size_t fs;
|
||||
void *p;
|
||||
sf_count_t n;
|
||||
|
||||
if (!u->memchunk.memblock) {
|
||||
|
||||
u->memchunk.memblock = pa_memblock_new(i->sink->core->mempool, length);
|
||||
u->memchunk.index = 0;
|
||||
|
||||
if (u->readf_function) {
|
||||
sf_count_t n;
|
||||
void *p;
|
||||
size_t fs = pa_frame_size(&i->sample_spec);
|
||||
|
||||
p = pa_memblock_acquire(u->memchunk.memblock);
|
||||
n = u->readf_function(u->sndfile, p, length/fs);
|
||||
pa_memblock_release(u->memchunk.memblock);
|
||||
|
||||
if (n <= 0)
|
||||
n = 0;
|
||||
|
||||
u->memchunk.length = n * fs;
|
||||
} else {
|
||||
sf_count_t n;
|
||||
void *p;
|
||||
|
||||
p = pa_memblock_acquire(u->memchunk.memblock);
|
||||
n = sf_read_raw(u->sndfile, p, length);
|
||||
pa_memblock_release(u->memchunk.memblock);
|
||||
|
||||
if (n <= 0)
|
||||
n = 0;
|
||||
|
||||
u->memchunk.length = n;
|
||||
}
|
||||
|
||||
if (u->memchunk.length <= 0) {
|
||||
|
||||
pa_memblock_unref(u->memchunk.memblock);
|
||||
pa_memchunk_reset(&u->memchunk);
|
||||
|
||||
pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u), FILE_STREAM_MESSAGE_UNLINK, NULL, 0, NULL, NULL);
|
||||
|
||||
sf_close(u->sndfile);
|
||||
u->sndfile = NULL;
|
||||
|
||||
return -1;
|
||||
}
|
||||
if (pa_memblockq_peek(u->memblockq, chunk) >= 0) {
|
||||
pa_memblockq_drop(u->memblockq, chunk->length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
pa_assert(u->memchunk.memblock);
|
||||
pa_assert(u->memchunk.length > 0);
|
||||
if (!u->sndfile)
|
||||
break;
|
||||
|
||||
if (u->drop < u->memchunk.length) {
|
||||
u->memchunk.index += u->drop;
|
||||
u->memchunk.length -= u->drop;
|
||||
u->drop = 0;
|
||||
tchunk.memblock = pa_memblock_new(i->sink->core->mempool, length);
|
||||
tchunk.index = 0;
|
||||
|
||||
p = pa_memblock_acquire(tchunk.memblock);
|
||||
|
||||
if (u->readf_function) {
|
||||
fs = pa_frame_size(&i->sample_spec);
|
||||
n = u->readf_function(u->sndfile, p, length/fs);
|
||||
} else {
|
||||
fs = 1;
|
||||
n = sf_read_raw(u->sndfile, p, length);
|
||||
}
|
||||
|
||||
pa_memblock_release(tchunk.memblock);
|
||||
|
||||
if (n <= 0) {
|
||||
pa_memblock_unref(tchunk.memblock);
|
||||
|
||||
sf_close(u->sndfile);
|
||||
u->sndfile = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
u->drop -= u->memchunk.length;
|
||||
pa_memblock_unref(u->memchunk.memblock);
|
||||
pa_memchunk_reset(&u->memchunk);
|
||||
tchunk.length = n * fs;
|
||||
|
||||
pa_memblockq_push(u->memblockq, &tchunk);
|
||||
pa_memblock_unref(tchunk.memblock);
|
||||
}
|
||||
|
||||
*chunk = u->memchunk;
|
||||
pa_memblock_ref(chunk->memblock);
|
||||
pa_log_debug("peek fail");
|
||||
|
||||
pa_assert(chunk->length > 0);
|
||||
pa_assert(u->drop <= 0);
|
||||
if (pa_sink_input_safe_to_remove(i)) {
|
||||
pa_log_debug("completed to play");
|
||||
|
||||
return 0;
|
||||
}
|
||||
pa_memblockq_free(u->memblockq);
|
||||
u->memblockq = NULL;
|
||||
|
||||
static void sink_input_drop_cb(pa_sink_input *i, size_t length) {
|
||||
pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u), FILE_STREAM_MESSAGE_UNLINK, NULL, 0, NULL, NULL);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
|
||||
file_stream *u;
|
||||
|
||||
pa_assert(i);
|
||||
pa_assert(length > 0);
|
||||
pa_sink_input_assert_ref(i);
|
||||
pa_assert(nbytes > 0);
|
||||
u = FILE_STREAM(i->userdata);
|
||||
file_stream_assert_ref(u);
|
||||
|
||||
if (u->memchunk.memblock) {
|
||||
if (!u->memblockq)
|
||||
return;
|
||||
|
||||
if (length < u->memchunk.length) {
|
||||
u->memchunk.index += length;
|
||||
u->memchunk.length -= length;
|
||||
return;
|
||||
}
|
||||
pa_memblockq_rewind(u->memblockq, nbytes);
|
||||
}
|
||||
|
||||
length -= u->memchunk.length;
|
||||
pa_memblock_unref(u->memchunk.memblock);
|
||||
pa_memchunk_reset(&u->memchunk);
|
||||
}
|
||||
static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
|
||||
file_stream *u;
|
||||
|
||||
u->drop += length;
|
||||
pa_sink_input_assert_ref(i);
|
||||
u = FILE_STREAM(i->userdata);
|
||||
file_stream_assert_ref(u);
|
||||
|
||||
if (!u->memblockq)
|
||||
return;
|
||||
|
||||
pa_memblockq_set_maxrewind(u->memblockq, nbytes);
|
||||
}
|
||||
|
||||
int pa_play_file(
|
||||
|
|
@ -237,10 +254,9 @@ int pa_play_file(
|
|||
u->parent.process_msg = file_stream_process_msg;
|
||||
u->core = sink->core;
|
||||
u->sink_input = NULL;
|
||||
pa_memchunk_reset(&u->memchunk);
|
||||
u->sndfile = NULL;
|
||||
u->readf_function = NULL;
|
||||
u->drop = 0;
|
||||
u->memblockq = NULL;
|
||||
|
||||
memset(&sfinfo, 0, sizeof(sfinfo));
|
||||
|
||||
|
|
@ -312,18 +328,26 @@ int pa_play_file(
|
|||
pa_sink_input_new_data_init(&data);
|
||||
data.sink = sink;
|
||||
data.driver = __FILE__;
|
||||
data.name = fname;
|
||||
pa_sink_input_new_data_set_sample_spec(&data, &ss);
|
||||
pa_sink_input_new_data_set_volume(&data, volume);
|
||||
pa_proplist_sets(data.proplist, PA_PROP_MEDIA_NAME, fname);
|
||||
pa_proplist_sets(data.proplist, PA_PROP_MEDIA_FILENAME, fname);
|
||||
|
||||
if (!(u->sink_input = pa_sink_input_new(sink->core, &data, 0)))
|
||||
u->sink_input = pa_sink_input_new(sink->core, &data, 0);
|
||||
pa_sink_input_new_data_done(&data);
|
||||
|
||||
if (!u->sink_input)
|
||||
goto fail;
|
||||
|
||||
u->sink_input->peek = sink_input_peek_cb;
|
||||
u->sink_input->drop = sink_input_drop_cb;
|
||||
u->sink_input->pop = sink_input_pop_cb;
|
||||
u->sink_input->process_rewind = sink_input_process_rewind_cb;
|
||||
u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
|
||||
u->sink_input->kill = sink_input_kill_cb;
|
||||
u->sink_input->state_change = sink_input_state_change_cb;
|
||||
u->sink_input->userdata = u;
|
||||
|
||||
u->memblockq = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, pa_frame_size(&ss), 1, 1, 0, NULL);
|
||||
|
||||
pa_sink_input_put(u->sink_input);
|
||||
|
||||
/* The reference to u is dangling here, because we want to keep
|
||||
|
|
|
|||
|
|
@ -32,12 +32,16 @@
|
|||
#include <pulse/utf8.h>
|
||||
#include <pulse/xmalloc.h>
|
||||
|
||||
#include <pulsecore/sample-util.h>
|
||||
#include <pulsecore/core-subscribe.h>
|
||||
#include <pulsecore/log.h>
|
||||
#include <pulsecore/namereg.h>
|
||||
#include <pulsecore/core-util.h>
|
||||
|
||||
#include "source-output.h"
|
||||
|
||||
#define MEMBLOCKQ_MAXLENGTH (32*1024*1024)
|
||||
|
||||
static PA_DEFINE_CHECK_TYPE(pa_source_output, pa_msgobject);
|
||||
|
||||
static void source_output_free(pa_object* mo);
|
||||
|
|
@ -47,9 +51,18 @@ pa_source_output_new_data* pa_source_output_new_data_init(pa_source_output_new_d
|
|||
|
||||
memset(data, 0, sizeof(*data));
|
||||
data->resample_method = PA_RESAMPLER_INVALID;
|
||||
data->proplist = pa_proplist_new();
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void pa_source_output_new_data_set_sample_spec(pa_source_output_new_data *data, const pa_sample_spec *spec) {
|
||||
pa_assert(data);
|
||||
|
||||
if ((data->sample_spec_is_set = !!spec))
|
||||
data->sample_spec = *spec;
|
||||
}
|
||||
|
||||
void pa_source_output_new_data_set_channel_map(pa_source_output_new_data *data, const pa_channel_map *map) {
|
||||
pa_assert(data);
|
||||
|
||||
|
|
@ -57,11 +70,25 @@ void pa_source_output_new_data_set_channel_map(pa_source_output_new_data *data,
|
|||
data->channel_map = *map;
|
||||
}
|
||||
|
||||
void pa_source_output_new_data_set_sample_spec(pa_source_output_new_data *data, const pa_sample_spec *spec) {
|
||||
void pa_source_output_new_data_done(pa_source_output_new_data *data) {
|
||||
pa_assert(data);
|
||||
|
||||
if ((data->sample_spec_is_set = !!spec))
|
||||
data->sample_spec = *spec;
|
||||
pa_proplist_free(data->proplist);
|
||||
}
|
||||
|
||||
static void reset_callbacks(pa_source_output *o) {
|
||||
pa_assert(o);
|
||||
|
||||
o->push = NULL;
|
||||
o->process_rewind = NULL;
|
||||
o->update_max_rewind = NULL;
|
||||
o->attach = NULL;
|
||||
o->detach = NULL;
|
||||
o->suspend = NULL;
|
||||
o->moved = NULL;
|
||||
o->kill = NULL;
|
||||
o->get_latency = NULL;
|
||||
o->state_change = NULL;
|
||||
}
|
||||
|
||||
pa_source_output* pa_source_output_new(
|
||||
|
|
@ -80,7 +107,6 @@ pa_source_output* pa_source_output_new(
|
|||
return NULL;
|
||||
|
||||
pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver));
|
||||
pa_return_null_if_fail(!data->name || pa_utf8_valid(data->name));
|
||||
|
||||
if (!data->source)
|
||||
data->source = pa_namereg_get(core, NULL, PA_NAMEREG_SOURCE, 1);
|
||||
|
|
@ -156,7 +182,7 @@ pa_source_output* pa_source_output_new(
|
|||
o->core = core;
|
||||
o->state = PA_SOURCE_OUTPUT_INIT;
|
||||
o->flags = flags;
|
||||
o->name = pa_xstrdup(data->name);
|
||||
o->proplist = pa_proplist_copy(data->proplist);
|
||||
o->driver = pa_xstrdup(data->driver);
|
||||
o->module = data->module;
|
||||
o->source = data->source;
|
||||
|
|
@ -166,26 +192,31 @@ pa_source_output* pa_source_output_new(
|
|||
o->sample_spec = data->sample_spec;
|
||||
o->channel_map = data->channel_map;
|
||||
|
||||
o->push = NULL;
|
||||
o->kill = NULL;
|
||||
o->get_latency = NULL;
|
||||
o->detach = NULL;
|
||||
o->attach = NULL;
|
||||
o->suspend = NULL;
|
||||
o->moved = NULL;
|
||||
reset_callbacks(o);
|
||||
o->userdata = NULL;
|
||||
|
||||
o->thread_info.state = o->state;
|
||||
o->thread_info.attached = FALSE;
|
||||
o->thread_info.sample_spec = o->sample_spec;
|
||||
o->thread_info.resampler = resampler;
|
||||
o->thread_info.requested_source_latency = (pa_usec_t) -1;
|
||||
|
||||
o->thread_info.delay_memblockq = pa_memblockq_new(
|
||||
0,
|
||||
MEMBLOCKQ_MAXLENGTH,
|
||||
0,
|
||||
pa_frame_size(&o->source->sample_spec),
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
&o->source->silence);
|
||||
|
||||
pa_assert_se(pa_idxset_put(core->source_outputs, o, &o->index) == 0);
|
||||
pa_assert_se(pa_idxset_put(o->source->outputs, pa_source_output_ref(o), NULL) == 0);
|
||||
|
||||
pa_log_info("Created output %u \"%s\" on %s with sample spec %s and channel map %s",
|
||||
o->index,
|
||||
o->name,
|
||||
pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_MEDIA_NAME)),
|
||||
o->source->name,
|
||||
pa_sample_spec_snprint(st, sizeof(st), &o->sample_spec),
|
||||
pa_channel_map_snprint(cm, sizeof(cm), &o->channel_map));
|
||||
|
|
@ -195,6 +226,17 @@ pa_source_output* pa_source_output_new(
|
|||
return o;
|
||||
}
|
||||
|
||||
static void update_n_corked(pa_source_output *o, pa_source_output_state_t state) {
|
||||
pa_assert(o);
|
||||
|
||||
if (o->state == PA_SOURCE_OUTPUT_CORKED && state != PA_SOURCE_OUTPUT_CORKED)
|
||||
pa_assert_se(o->source->n_corked -- >= 1);
|
||||
else if (o->state != PA_SOURCE_OUTPUT_CORKED && state == PA_SOURCE_OUTPUT_CORKED)
|
||||
o->source->n_corked++;
|
||||
|
||||
pa_source_update_status(o->source);
|
||||
}
|
||||
|
||||
static int source_output_set_state(pa_source_output *o, pa_source_output_state_t state) {
|
||||
pa_assert(o);
|
||||
|
||||
|
|
@ -204,13 +246,7 @@ static int source_output_set_state(pa_source_output *o, pa_source_output_state_t
|
|||
if (pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) < 0)
|
||||
return -1;
|
||||
|
||||
if (o->state == PA_SOURCE_OUTPUT_CORKED && state != PA_SOURCE_OUTPUT_CORKED)
|
||||
pa_assert_se(o->source->n_corked -- >= 1);
|
||||
else if (o->state != PA_SOURCE_OUTPUT_CORKED && state == PA_SOURCE_OUTPUT_CORKED)
|
||||
o->source->n_corked++;
|
||||
|
||||
pa_source_update_status(o->source);
|
||||
|
||||
update_n_corked(o, state);
|
||||
o->state = state;
|
||||
|
||||
if (state != PA_SOURCE_OUTPUT_UNLINKED)
|
||||
|
|
@ -228,7 +264,7 @@ void pa_source_output_unlink(pa_source_output*o) {
|
|||
|
||||
pa_source_output_ref(o);
|
||||
|
||||
linked = PA_SOURCE_OUTPUT_LINKED(o->state);
|
||||
linked = PA_SOURCE_OUTPUT_IS_LINKED(o->state);
|
||||
|
||||
if (linked)
|
||||
pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK], o);
|
||||
|
|
@ -237,20 +273,14 @@ void pa_source_output_unlink(pa_source_output*o) {
|
|||
if (pa_idxset_remove_by_data(o->source->outputs, o, NULL))
|
||||
pa_source_output_unref(o);
|
||||
|
||||
if (linked) {
|
||||
pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL);
|
||||
source_output_set_state(o, PA_SOURCE_OUTPUT_UNLINKED);
|
||||
pa_source_update_status(o->source);
|
||||
} else
|
||||
o->state = PA_SOURCE_OUTPUT_UNLINKED;
|
||||
update_n_corked(o, PA_SOURCE_OUTPUT_UNLINKED);
|
||||
o->state = PA_SOURCE_OUTPUT_UNLINKED;
|
||||
|
||||
o->push = NULL;
|
||||
o->kill = NULL;
|
||||
o->get_latency = NULL;
|
||||
o->attach = NULL;
|
||||
o->detach = NULL;
|
||||
o->suspend = NULL;
|
||||
o->moved = NULL;
|
||||
if (linked)
|
||||
if (o->source->asyncmsgq)
|
||||
pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL);
|
||||
|
||||
reset_callbacks(o);
|
||||
|
||||
if (linked) {
|
||||
pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_REMOVE, o->index);
|
||||
|
|
@ -264,45 +294,50 @@ void pa_source_output_unlink(pa_source_output*o) {
|
|||
static void source_output_free(pa_object* mo) {
|
||||
pa_source_output *o = PA_SOURCE_OUTPUT(mo);
|
||||
|
||||
pa_assert(o);
|
||||
pa_assert(pa_source_output_refcnt(o) == 0);
|
||||
|
||||
if (PA_SOURCE_OUTPUT_LINKED(o->state))
|
||||
if (PA_SOURCE_OUTPUT_IS_LINKED(o->state))
|
||||
pa_source_output_unlink(o);
|
||||
|
||||
pa_log_info("Freeing output %u \"%s\"", o->index, o->name);
|
||||
pa_log_info("Freeing output %u \"%s\"", o->index, pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_MEDIA_NAME)));
|
||||
|
||||
pa_assert(!o->thread_info.attached);
|
||||
|
||||
if (o->thread_info.delay_memblockq)
|
||||
pa_memblockq_free(o->thread_info.delay_memblockq);
|
||||
|
||||
if (o->thread_info.resampler)
|
||||
pa_resampler_free(o->thread_info.resampler);
|
||||
|
||||
pa_xfree(o->name);
|
||||
if (o->proplist)
|
||||
pa_proplist_free(o->proplist);
|
||||
|
||||
pa_xfree(o->driver);
|
||||
pa_xfree(o);
|
||||
}
|
||||
|
||||
void pa_source_output_put(pa_source_output *o) {
|
||||
pa_source_output_state_t state;
|
||||
pa_source_output_assert_ref(o);
|
||||
|
||||
pa_assert(o->state == PA_SOURCE_OUTPUT_INIT);
|
||||
pa_assert(o->push);
|
||||
|
||||
o->thread_info.state = o->state = o->flags & PA_SOURCE_OUTPUT_START_CORKED ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING;
|
||||
state = o->flags & PA_SOURCE_OUTPUT_START_CORKED ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING;
|
||||
|
||||
if (o->state == PA_SOURCE_OUTPUT_CORKED)
|
||||
o->source->n_corked++;
|
||||
update_n_corked(o, state);
|
||||
o->state = state;
|
||||
|
||||
pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_ADD_OUTPUT, o, 0, NULL);
|
||||
pa_source_update_status(o->source);
|
||||
|
||||
pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW, o->index);
|
||||
|
||||
pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT], o);
|
||||
}
|
||||
|
||||
void pa_source_output_kill(pa_source_output*o) {
|
||||
pa_source_output_assert_ref(o);
|
||||
pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state));
|
||||
pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
|
||||
|
||||
if (o->kill)
|
||||
o->kill(o);
|
||||
|
|
@ -312,7 +347,7 @@ pa_usec_t pa_source_output_get_latency(pa_source_output *o) {
|
|||
pa_usec_t r = 0;
|
||||
|
||||
pa_source_output_assert_ref(o);
|
||||
pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state));
|
||||
pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
|
||||
|
||||
if (pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY, &r, 0, NULL) < 0)
|
||||
r = 0;
|
||||
|
|
@ -325,42 +360,169 @@ pa_usec_t pa_source_output_get_latency(pa_source_output *o) {
|
|||
|
||||
/* Called from thread context */
|
||||
void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) {
|
||||
pa_memchunk rchunk;
|
||||
size_t length;
|
||||
size_t limit, mbs = 0;
|
||||
|
||||
pa_source_output_assert_ref(o);
|
||||
pa_assert(PA_SOURCE_OUTPUT_LINKED(o->thread_info.state));
|
||||
pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state));
|
||||
pa_assert(chunk);
|
||||
pa_assert(chunk->length);
|
||||
pa_assert(pa_frame_aligned(chunk->length, &o->source->sample_spec));
|
||||
|
||||
if (!o->push || o->state == PA_SOURCE_OUTPUT_CORKED)
|
||||
return;
|
||||
|
||||
pa_assert(o->state == PA_SOURCE_OUTPUT_RUNNING);
|
||||
|
||||
if (!o->thread_info.resampler) {
|
||||
o->push(o, chunk);
|
||||
return;
|
||||
if (pa_memblockq_push(o->thread_info.delay_memblockq, chunk) < 0) {
|
||||
pa_log_debug("Delay queue overflow!");
|
||||
pa_memblockq_seek(o->thread_info.delay_memblockq, chunk->length, PA_SEEK_RELATIVE);
|
||||
}
|
||||
|
||||
pa_resampler_run(o->thread_info.resampler, chunk, &rchunk);
|
||||
if (!rchunk.length)
|
||||
limit = o->process_rewind ? 0 : o->source->thread_info.max_rewind;
|
||||
|
||||
/* Implement the delay queue */
|
||||
while ((length = pa_memblockq_get_length(o->thread_info.delay_memblockq)) > limit) {
|
||||
pa_memchunk qchunk;
|
||||
|
||||
length -= limit;
|
||||
|
||||
pa_assert_se(pa_memblockq_peek(o->thread_info.delay_memblockq, &qchunk) >= 0);
|
||||
|
||||
if (qchunk.length > length)
|
||||
qchunk.length = length;
|
||||
|
||||
pa_assert(qchunk.length > 0);
|
||||
|
||||
if (!o->thread_info.resampler)
|
||||
o->push(o, &qchunk);
|
||||
else {
|
||||
pa_memchunk rchunk;
|
||||
|
||||
if (mbs == 0)
|
||||
mbs = pa_resampler_max_block_size(o->thread_info.resampler);
|
||||
|
||||
if (qchunk.length > mbs)
|
||||
qchunk.length = mbs;
|
||||
|
||||
pa_resampler_run(o->thread_info.resampler, &qchunk, &rchunk);
|
||||
|
||||
if (rchunk.length > 0)
|
||||
o->push(o, &rchunk);
|
||||
|
||||
pa_memblock_unref(rchunk.memblock);
|
||||
}
|
||||
|
||||
pa_memblock_unref(qchunk.memblock);
|
||||
pa_memblockq_drop(o->thread_info.delay_memblockq, qchunk.length);
|
||||
}
|
||||
}
|
||||
|
||||
/* Called from thread context */
|
||||
void pa_source_output_process_rewind(pa_source_output *o, size_t nbytes /* in sink sample spec */) {
|
||||
pa_source_output_assert_ref(o);
|
||||
|
||||
pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
|
||||
pa_assert(pa_frame_aligned(nbytes, &o->source->sample_spec));
|
||||
|
||||
if (nbytes <= 0)
|
||||
return;
|
||||
|
||||
pa_assert(rchunk.memblock);
|
||||
o->push(o, &rchunk);
|
||||
pa_memblock_unref(rchunk.memblock);
|
||||
if (o->process_rewind) {
|
||||
pa_assert(pa_memblockq_get_length(o->thread_info.delay_memblockq) == 0);
|
||||
|
||||
if (o->thread_info.resampler)
|
||||
nbytes = pa_resampler_result(o->thread_info.resampler, nbytes);
|
||||
|
||||
pa_log_debug("Have to rewind %lu bytes on implementor.", (unsigned long) nbytes);
|
||||
|
||||
if (nbytes > 0)
|
||||
o->process_rewind(o, nbytes);
|
||||
|
||||
if (o->thread_info.resampler)
|
||||
pa_resampler_reset(o->thread_info.resampler);
|
||||
|
||||
} else
|
||||
pa_memblockq_rewind(o->thread_info.delay_memblockq, nbytes);
|
||||
}
|
||||
|
||||
/* Called from thread context */
|
||||
void pa_source_output_update_max_rewind(pa_source_output *o, size_t nbytes /* in the source's sample spec */) {
|
||||
pa_source_output_assert_ref(o);
|
||||
pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state));
|
||||
pa_assert(pa_frame_aligned(nbytes, &o->source->sample_spec));
|
||||
|
||||
if (o->update_max_rewind)
|
||||
o->update_max_rewind(o, o->thread_info.resampler ? pa_resampler_result(o->thread_info.resampler, nbytes) : nbytes);
|
||||
}
|
||||
|
||||
static pa_usec_t fixup_latency(pa_source *s, pa_usec_t usec) {
|
||||
pa_source_assert_ref(s);
|
||||
|
||||
if (usec == (pa_usec_t) -1)
|
||||
return usec;
|
||||
|
||||
if (s->max_latency > 0 && usec > s->max_latency)
|
||||
usec = s->max_latency;
|
||||
|
||||
if (s->min_latency > 0 && usec < s->min_latency)
|
||||
usec = s->min_latency;
|
||||
|
||||
return usec;
|
||||
}
|
||||
|
||||
pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output *o, pa_usec_t usec) {
|
||||
pa_source_output_assert_ref(o);
|
||||
|
||||
usec = fixup_latency(o->source, usec);
|
||||
|
||||
o->thread_info.requested_source_latency = usec;
|
||||
pa_source_invalidate_requested_latency(o->source);
|
||||
|
||||
return usec;
|
||||
}
|
||||
|
||||
pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t usec) {
|
||||
pa_source_output_assert_ref(o);
|
||||
|
||||
usec = fixup_latency(o->source, usec);
|
||||
|
||||
if (PA_SOURCE_OUTPUT_IS_LINKED(o->state))
|
||||
pa_asyncmsgq_post(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY, NULL, (int64_t) usec, NULL, NULL);
|
||||
else {
|
||||
/* If this sink input is not realized yet, we have to touch
|
||||
* the thread info data directly */
|
||||
o->thread_info.requested_source_latency = usec;
|
||||
o->source->thread_info.requested_latency_valid = FALSE;
|
||||
}
|
||||
|
||||
return usec;
|
||||
}
|
||||
|
||||
pa_usec_t pa_source_output_get_requested_latency(pa_source_output *o) {
|
||||
pa_usec_t usec = 0;
|
||||
|
||||
pa_source_output_assert_ref(o);
|
||||
|
||||
if (PA_SOURCE_OUTPUT_IS_LINKED(o->state))
|
||||
pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL);
|
||||
else
|
||||
/* If this sink input is not realized yet, we have to touch
|
||||
* the thread info data directly */
|
||||
usec = o->thread_info.requested_source_latency;
|
||||
|
||||
return usec;
|
||||
}
|
||||
|
||||
void pa_source_output_cork(pa_source_output *o, pa_bool_t b) {
|
||||
pa_source_output_assert_ref(o);
|
||||
pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state));
|
||||
pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
|
||||
|
||||
source_output_set_state(o, b ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING);
|
||||
}
|
||||
|
||||
int pa_source_output_set_rate(pa_source_output *o, uint32_t rate) {
|
||||
pa_source_output_assert_ref(o);
|
||||
pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state));
|
||||
pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
|
||||
pa_return_val_if_fail(o->thread_info.resampler, -1);
|
||||
|
||||
if (o->sample_spec.rate == rate)
|
||||
|
|
@ -375,19 +537,24 @@ int pa_source_output_set_rate(pa_source_output *o, uint32_t rate) {
|
|||
}
|
||||
|
||||
void pa_source_output_set_name(pa_source_output *o, const char *name) {
|
||||
const char *old;
|
||||
pa_source_output_assert_ref(o);
|
||||
|
||||
if (!o->name && !name)
|
||||
if (!name && !pa_proplist_contains(o->proplist, PA_PROP_MEDIA_NAME))
|
||||
return;
|
||||
|
||||
if (o->name && name && !strcmp(o->name, name))
|
||||
old = pa_proplist_gets(o->proplist, PA_PROP_MEDIA_NAME);
|
||||
|
||||
if (old && name && !strcmp(old, name))
|
||||
return;
|
||||
|
||||
pa_xfree(o->name);
|
||||
o->name = pa_xstrdup(name);
|
||||
if (name)
|
||||
pa_proplist_sets(o->proplist, PA_PROP_MEDIA_NAME, name);
|
||||
else
|
||||
pa_proplist_unset(o->proplist, PA_PROP_MEDIA_NAME);
|
||||
|
||||
if (PA_SOURCE_OUTPUT_LINKED(o->state)) {
|
||||
pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NAME_CHANGED], o);
|
||||
if (PA_SOURCE_OUTPUT_IS_LINKED(o->state)) {
|
||||
pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED], o);
|
||||
pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
|
||||
}
|
||||
}
|
||||
|
|
@ -400,11 +567,11 @@ pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o) {
|
|||
|
||||
int pa_source_output_move_to(pa_source_output *o, pa_source *dest) {
|
||||
pa_source *origin;
|
||||
pa_resampler *new_resampler = NULL;
|
||||
pa_resampler *new_resampler;
|
||||
pa_source_output_move_hook_data hook_data;
|
||||
|
||||
pa_source_output_assert_ref(o);
|
||||
pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state));
|
||||
pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
|
||||
pa_source_assert_ref(dest);
|
||||
|
||||
origin = o->source;
|
||||
|
|
@ -444,7 +611,8 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) {
|
|||
pa_log_warn("Unsupported resampling operation.");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
} else
|
||||
new_resampler = NULL;
|
||||
|
||||
hook_data.source_output = o;
|
||||
hook_data.destination = dest;
|
||||
|
|
@ -467,13 +635,25 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) {
|
|||
if (o->thread_info.resampler)
|
||||
pa_resampler_free(o->thread_info.resampler);
|
||||
o->thread_info.resampler = new_resampler;
|
||||
}
|
||||
|
||||
pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_ADD_OUTPUT, o, 0, NULL);
|
||||
pa_memblockq_free(o->thread_info.delay_memblockq);
|
||||
|
||||
o->thread_info.delay_memblockq = pa_memblockq_new(
|
||||
0,
|
||||
MEMBLOCKQ_MAXLENGTH,
|
||||
0,
|
||||
pa_frame_size(&o->source->sample_spec),
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
&o->source->silence);
|
||||
}
|
||||
|
||||
pa_source_update_status(origin);
|
||||
pa_source_update_status(dest);
|
||||
|
||||
pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_ADD_OUTPUT, o, 0, NULL);
|
||||
|
||||
if (o->moved)
|
||||
o->moved(o);
|
||||
|
||||
|
|
@ -487,26 +667,54 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
void pa_source_output_set_state_within_thread(pa_source_output *o, pa_source_output_state_t state) {
|
||||
pa_source_output_assert_ref(o);
|
||||
|
||||
if (state == o->thread_info.state)
|
||||
return;
|
||||
|
||||
if (o->state_change)
|
||||
o->state_change(o, state);
|
||||
|
||||
o->thread_info.state = state;
|
||||
}
|
||||
|
||||
/* Called from thread context */
|
||||
int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int64_t offset, pa_memchunk* chunk) {
|
||||
pa_source_output *o = PA_SOURCE_OUTPUT(mo);
|
||||
|
||||
pa_source_output_assert_ref(o);
|
||||
pa_assert(PA_SOURCE_OUTPUT_LINKED(o->thread_info.state));
|
||||
pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state));
|
||||
|
||||
switch (code) {
|
||||
|
||||
case PA_SOURCE_OUTPUT_MESSAGE_SET_RATE: {
|
||||
|
||||
o->thread_info.sample_spec.rate = PA_PTR_TO_UINT(userdata);
|
||||
pa_resampler_set_output_rate(o->thread_info.resampler, PA_PTR_TO_UINT(userdata));
|
||||
case PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY: {
|
||||
pa_usec_t *r = userdata;
|
||||
|
||||
*r += pa_bytes_to_usec(pa_memblockq_get_length(o->thread_info.delay_memblockq), &o->source->sample_spec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
case PA_SOURCE_OUTPUT_MESSAGE_SET_STATE: {
|
||||
o->thread_info.state = PA_PTR_TO_UINT(userdata);
|
||||
case PA_SOURCE_OUTPUT_MESSAGE_SET_RATE:
|
||||
|
||||
o->thread_info.sample_spec.rate = PA_PTR_TO_UINT(userdata);
|
||||
pa_resampler_set_output_rate(o->thread_info.resampler, PA_PTR_TO_UINT(userdata));
|
||||
return 0;
|
||||
|
||||
case PA_SOURCE_OUTPUT_MESSAGE_SET_STATE:
|
||||
|
||||
pa_source_output_set_state_within_thread(o, PA_PTR_TO_UINT(userdata));
|
||||
return 0;
|
||||
|
||||
case PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY:
|
||||
|
||||
pa_source_output_set_requested_latency_within_thread(o, (pa_usec_t) offset);
|
||||
return 0;
|
||||
|
||||
case PA_SINK_INPUT_MESSAGE_GET_REQUESTED_LATENCY: {
|
||||
pa_usec_t *r = userdata;
|
||||
|
||||
*r = o->thread_info.requested_source_latency;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ typedef enum pa_source_output_state {
|
|||
PA_SOURCE_OUTPUT_UNLINKED
|
||||
} pa_source_output_state_t;
|
||||
|
||||
static inline pa_bool_t PA_SOURCE_OUTPUT_LINKED(pa_source_output_state_t x) {
|
||||
static inline pa_bool_t PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_state_t x) {
|
||||
return x == PA_SOURCE_OUTPUT_RUNNING || x == PA_SOURCE_OUTPUT_CORKED;
|
||||
}
|
||||
|
||||
|
|
@ -62,10 +62,12 @@ struct pa_source_output {
|
|||
|
||||
uint32_t index;
|
||||
pa_core *core;
|
||||
|
||||
pa_source_output_state_t state;
|
||||
pa_source_output_flags_t flags;
|
||||
|
||||
char *name, *driver; /* may be NULL */
|
||||
pa_proplist *proplist;
|
||||
char *driver; /* may be NULL */
|
||||
pa_module *module; /* may be NULL */
|
||||
pa_client *client; /* may be NULL */
|
||||
|
||||
|
|
@ -74,10 +76,20 @@ struct pa_source_output {
|
|||
pa_sample_spec sample_spec;
|
||||
pa_channel_map channel_map;
|
||||
|
||||
pa_resample_method_t resample_method;
|
||||
|
||||
/* Pushes a new memchunk into the output. Called from IO thread
|
||||
* context. */
|
||||
void (*push)(pa_source_output *o, const pa_memchunk *chunk);
|
||||
|
||||
/* Only relevant for monitor sources right now: called when the
|
||||
* recorded stream is rewound. Called from IO context*/
|
||||
void (*process_rewind)(pa_source_output *o, size_t nbytes);
|
||||
|
||||
/* Called whenever the maximum rewindable size of the source
|
||||
* changes. Called from IO thread context. */
|
||||
void (*update_max_rewind) (pa_source_output *o, size_t nbytes); /* may be NULL */
|
||||
|
||||
/* If non-NULL this function is called when the output is first
|
||||
* connected to a source. Called from IO thread context */
|
||||
void (*attach) (pa_source_output *o); /* may be NULL */
|
||||
|
|
@ -86,14 +98,14 @@ struct pa_source_output {
|
|||
* disconnected from its source. Called from IO thread context */
|
||||
void (*detach) (pa_source_output *o); /* may be NULL */
|
||||
|
||||
/* If non-NULL called whenever the the source this output is attached
|
||||
* to changes. Called from main context */
|
||||
void (*moved) (pa_source_output *o); /* may be NULL */
|
||||
|
||||
/* If non-NULL called whenever the the source this output is attached
|
||||
* to suspends or resumes. Called from main context */
|
||||
void (*suspend) (pa_source_output *o, pa_bool_t b); /* may be NULL */
|
||||
|
||||
/* If non-NULL called whenever the the source this output is attached
|
||||
* to changes. Called from main context */
|
||||
void (*moved) (pa_source_output *o); /* may be NULL */
|
||||
|
||||
/* Supposed to unlink and destroy this stream. Called from main
|
||||
* context. */
|
||||
void (*kill)(pa_source_output* o); /* may be NULL */
|
||||
|
|
@ -104,7 +116,9 @@ struct pa_source_output {
|
|||
thread instead. */
|
||||
pa_usec_t (*get_latency) (pa_source_output *o); /* may be NULL */
|
||||
|
||||
pa_resample_method_t resample_method;
|
||||
/* If non_NULL this function is called from thread context if the
|
||||
* state changes. The old state is found in thread_info.state. */
|
||||
void (*state_change) (pa_source_output *o, pa_source_output_state_t state); /* may be NULL */
|
||||
|
||||
struct {
|
||||
pa_source_output_state_t state;
|
||||
|
|
@ -114,6 +128,13 @@ struct pa_source_output {
|
|||
pa_sample_spec sample_spec;
|
||||
|
||||
pa_resampler* resampler; /* may be NULL */
|
||||
|
||||
/* We maintain a delay memblockq here for source outputs that
|
||||
* don't implement rewind() */
|
||||
pa_memblockq *delay_memblockq;
|
||||
|
||||
/* The requested latency for the source */
|
||||
pa_usec_t requested_source_latency;
|
||||
} thread_info;
|
||||
|
||||
void *userdata;
|
||||
|
|
@ -126,11 +147,15 @@ enum {
|
|||
PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY,
|
||||
PA_SOURCE_OUTPUT_MESSAGE_SET_RATE,
|
||||
PA_SOURCE_OUTPUT_MESSAGE_SET_STATE,
|
||||
PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY,
|
||||
PA_SOURCE_OUTPUT_MESSAGE_GET_REQUESTED_LATENCY,
|
||||
PA_SOURCE_OUTPUT_MESSAGE_MAX
|
||||
};
|
||||
|
||||
typedef struct pa_source_output_new_data {
|
||||
const char *name, *driver;
|
||||
pa_proplist *proplist;
|
||||
|
||||
const char *driver;
|
||||
pa_module *module;
|
||||
pa_client *client;
|
||||
|
||||
|
|
@ -144,16 +169,16 @@ typedef struct pa_source_output_new_data {
|
|||
pa_resample_method_t resample_method;
|
||||
} pa_source_output_new_data;
|
||||
|
||||
pa_source_output_new_data* pa_source_output_new_data_init(pa_source_output_new_data *data);
|
||||
void pa_source_output_new_data_set_sample_spec(pa_source_output_new_data *data, const pa_sample_spec *spec);
|
||||
void pa_source_output_new_data_set_channel_map(pa_source_output_new_data *data, const pa_channel_map *map);
|
||||
void pa_source_output_new_data_done(pa_source_output_new_data *data);
|
||||
|
||||
typedef struct pa_source_output_move_hook_data {
|
||||
pa_source_output *source_output;
|
||||
pa_source *destination;
|
||||
} pa_source_output_move_hook_data;
|
||||
|
||||
pa_source_output_new_data* pa_source_output_new_data_init(pa_source_output_new_data *data);
|
||||
void pa_source_output_new_data_set_sample_spec(pa_source_output_new_data *data, const pa_sample_spec *spec);
|
||||
void pa_source_output_new_data_set_channel_map(pa_source_output_new_data *data, const pa_channel_map *map);
|
||||
void pa_source_output_new_data_set_volume(pa_source_output_new_data *data, const pa_cvolume *volume);
|
||||
|
||||
/* To be called by the implementing module only */
|
||||
|
||||
pa_source_output* pa_source_output_new(
|
||||
|
|
@ -166,6 +191,12 @@ void pa_source_output_unlink(pa_source_output*o);
|
|||
|
||||
void pa_source_output_set_name(pa_source_output *i, const char *name);
|
||||
|
||||
pa_usec_t pa_source_output_set_requested_latency(pa_source_output *i, pa_usec_t usec);
|
||||
|
||||
void pa_source_output_cork(pa_source_output *i, pa_bool_t b);
|
||||
|
||||
int pa_source_output_set_rate(pa_source_output *o, uint32_t rate);
|
||||
|
||||
/* Callable by everyone */
|
||||
|
||||
/* External code may request disconnection with this funcion */
|
||||
|
|
@ -173,19 +204,24 @@ void pa_source_output_kill(pa_source_output*o);
|
|||
|
||||
pa_usec_t pa_source_output_get_latency(pa_source_output *i);
|
||||
|
||||
void pa_source_output_cork(pa_source_output *i, pa_bool_t b);
|
||||
|
||||
int pa_source_output_set_rate(pa_source_output *o, uint32_t rate);
|
||||
|
||||
pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o);
|
||||
|
||||
int pa_source_output_move_to(pa_source_output *o, pa_source *dest);
|
||||
|
||||
#define pa_source_output_get_state(o) ((o)->state)
|
||||
|
||||
pa_usec_t pa_source_output_get_requested_latency(pa_source_output *o);
|
||||
|
||||
/* To be used exclusively by the source driver thread */
|
||||
|
||||
void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk);
|
||||
void pa_source_output_process_rewind(pa_source_output *o, size_t nbytes);
|
||||
void pa_source_output_update_max_rewind(pa_source_output *o, size_t nbytes);
|
||||
|
||||
void pa_source_output_set_state_within_thread(pa_source_output *o, pa_source_output_state_t state);
|
||||
|
||||
int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
|
||||
|
||||
pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output *o, pa_usec_t usec);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
|
||||
#include <pulse/utf8.h>
|
||||
#include <pulse/xmalloc.h>
|
||||
#include <pulse/timeval.h>
|
||||
|
||||
#include <pulsecore/source-output.h>
|
||||
#include <pulsecore/namereg.h>
|
||||
|
|
@ -41,85 +42,177 @@
|
|||
|
||||
#include "source.h"
|
||||
|
||||
#define DEFAULT_MIN_LATENCY (4*PA_USEC_PER_MSEC)
|
||||
|
||||
static PA_DEFINE_CHECK_TYPE(pa_source, pa_msgobject);
|
||||
|
||||
static void source_free(pa_object *o);
|
||||
|
||||
pa_source_new_data* pa_source_new_data_init(pa_source_new_data *data) {
|
||||
pa_assert(data);
|
||||
|
||||
memset(data, 0, sizeof(*data));
|
||||
data->proplist = pa_proplist_new();
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void pa_source_new_data_set_name(pa_source_new_data *data, const char *name) {
|
||||
pa_assert(data);
|
||||
|
||||
pa_xfree(data->name);
|
||||
data->name = pa_xstrdup(name);
|
||||
}
|
||||
|
||||
void pa_source_new_data_set_sample_spec(pa_source_new_data *data, const pa_sample_spec *spec) {
|
||||
pa_assert(data);
|
||||
|
||||
if ((data->sample_spec_is_set = !!spec))
|
||||
data->sample_spec = *spec;
|
||||
}
|
||||
|
||||
void pa_source_new_data_set_channel_map(pa_source_new_data *data, const pa_channel_map *map) {
|
||||
pa_assert(data);
|
||||
|
||||
if ((data->channel_map_is_set = !!map))
|
||||
data->channel_map = *map;
|
||||
}
|
||||
|
||||
void pa_source_new_data_set_volume(pa_source_new_data *data, const pa_cvolume *volume) {
|
||||
pa_assert(data);
|
||||
|
||||
if ((data->volume_is_set = !!volume))
|
||||
data->volume = *volume;
|
||||
}
|
||||
|
||||
void pa_source_new_data_set_muted(pa_source_new_data *data, pa_bool_t mute) {
|
||||
pa_assert(data);
|
||||
|
||||
data->muted_is_set = TRUE;
|
||||
data->muted = !!mute;
|
||||
}
|
||||
|
||||
void pa_source_new_data_done(pa_source_new_data *data) {
|
||||
pa_assert(data);
|
||||
|
||||
pa_xfree(data->name);
|
||||
pa_proplist_free(data->proplist);
|
||||
}
|
||||
|
||||
static void reset_callbacks(pa_source *s) {
|
||||
pa_assert(s);
|
||||
|
||||
s->set_state = NULL;
|
||||
s->get_volume = NULL;
|
||||
s->set_volume = NULL;
|
||||
s->get_mute = NULL;
|
||||
s->set_mute = NULL;
|
||||
s->update_requested_latency = NULL;
|
||||
}
|
||||
|
||||
pa_source* pa_source_new(
|
||||
pa_core *core,
|
||||
const char *driver,
|
||||
const char *name,
|
||||
int fail,
|
||||
const pa_sample_spec *spec,
|
||||
const pa_channel_map *map) {
|
||||
pa_source_new_data *data,
|
||||
pa_source_flags_t flags) {
|
||||
|
||||
pa_source *s;
|
||||
char st[256];
|
||||
pa_channel_map tmap;
|
||||
const char *name;
|
||||
char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
|
||||
|
||||
pa_assert(core);
|
||||
pa_assert(name);
|
||||
pa_assert(spec);
|
||||
|
||||
pa_return_null_if_fail(pa_sample_spec_valid(spec));
|
||||
|
||||
if (!map)
|
||||
pa_return_null_if_fail(map = pa_channel_map_init_auto(&tmap, spec->channels, PA_CHANNEL_MAP_DEFAULT));
|
||||
|
||||
pa_return_null_if_fail(map && pa_channel_map_valid(map));
|
||||
pa_return_null_if_fail(map->channels == spec->channels);
|
||||
pa_return_null_if_fail(!driver || pa_utf8_valid(driver));
|
||||
pa_return_null_if_fail(pa_utf8_valid(name) && *name);
|
||||
|
||||
s = pa_msgobject_new(pa_source);
|
||||
|
||||
if (!(name = pa_namereg_register(core, name, PA_NAMEREG_SOURCE, s, fail))) {
|
||||
if (!(name = pa_namereg_register(core, data->name, PA_NAMEREG_SOURCE, s, data->namereg_fail))) {
|
||||
pa_xfree(s);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_NEW], data) < 0) {
|
||||
pa_xfree(s);
|
||||
pa_namereg_unregister(core, name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver));
|
||||
pa_return_null_if_fail(data->name && pa_utf8_valid(data->name) && data->name[0]);
|
||||
|
||||
pa_return_null_if_fail(data->sample_spec_is_set && pa_sample_spec_valid(&data->sample_spec));
|
||||
|
||||
if (!data->channel_map_is_set)
|
||||
pa_return_null_if_fail(pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT));
|
||||
|
||||
pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map));
|
||||
pa_return_null_if_fail(data->channel_map.channels == data->sample_spec.channels);
|
||||
|
||||
if (!data->volume_is_set)
|
||||
pa_cvolume_reset(&data->volume, data->sample_spec.channels);
|
||||
|
||||
pa_return_null_if_fail(pa_cvolume_valid(&data->volume));
|
||||
pa_return_null_if_fail(data->volume.channels == data->sample_spec.channels);
|
||||
|
||||
if (!data->muted_is_set)
|
||||
data->muted = FALSE;
|
||||
|
||||
if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_FIXATE], data) < 0) {
|
||||
pa_xfree(s);
|
||||
pa_namereg_unregister(core, name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
s->parent.parent.free = source_free;
|
||||
s->parent.process_msg = pa_source_process_msg;
|
||||
|
||||
s->core = core;
|
||||
s->state = PA_SOURCE_INIT;
|
||||
s->flags = 0;
|
||||
s->flags = flags;
|
||||
s->name = pa_xstrdup(name);
|
||||
s->description = NULL;
|
||||
s->driver = pa_xstrdup(driver);
|
||||
s->module = NULL;
|
||||
s->proplist = pa_proplist_copy(data->proplist);
|
||||
s->driver = pa_xstrdup(data->driver);
|
||||
s->module = data->module;
|
||||
|
||||
s->sample_spec = *spec;
|
||||
s->channel_map = *map;
|
||||
s->sample_spec = data->sample_spec;
|
||||
s->channel_map = data->channel_map;
|
||||
|
||||
s->outputs = pa_idxset_new(NULL, NULL);
|
||||
s->n_corked = 0;
|
||||
s->monitor_of = NULL;
|
||||
|
||||
pa_cvolume_reset(&s->volume, spec->channels);
|
||||
s->muted = FALSE;
|
||||
s->volume = data->volume;
|
||||
s->muted = data->muted;
|
||||
s->refresh_volume = s->refresh_muted = FALSE;
|
||||
|
||||
s->get_latency = NULL;
|
||||
s->set_volume = NULL;
|
||||
s->get_volume = NULL;
|
||||
s->set_mute = NULL;
|
||||
s->get_mute = NULL;
|
||||
s->set_state = NULL;
|
||||
reset_callbacks(s);
|
||||
s->userdata = NULL;
|
||||
|
||||
s->asyncmsgq = NULL;
|
||||
s->rtpoll = NULL;
|
||||
|
||||
pa_assert_se(pa_idxset_put(core->sources, s, &s->index) >= 0);
|
||||
pa_silence_memchunk_get(
|
||||
&core->silence_cache,
|
||||
core->mempool,
|
||||
&s->silence,
|
||||
&s->sample_spec,
|
||||
0);
|
||||
|
||||
pa_sample_spec_snprint(st, sizeof(st), spec);
|
||||
pa_log_info("Created source %u \"%s\" with sample spec \"%s\"", s->index, s->name, st);
|
||||
s->min_latency = DEFAULT_MIN_LATENCY;
|
||||
s->max_latency = s->min_latency;
|
||||
|
||||
s->thread_info.outputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
|
||||
s->thread_info.soft_volume = s->volume;
|
||||
s->thread_info.soft_muted = s->muted;
|
||||
s->thread_info.state = s->state;
|
||||
s->thread_info.max_rewind = 0;
|
||||
s->thread_info.requested_latency_valid = FALSE;
|
||||
s->thread_info.requested_latency = 0;
|
||||
|
||||
pa_assert_se(pa_idxset_put(core->sources, s, &s->index) >= 0);
|
||||
|
||||
pa_log_info("Created source %u \"%s\" with sample spec %s and channel map %s",
|
||||
s->index,
|
||||
s->name,
|
||||
pa_sample_spec_snprint(st, sizeof(st), &s->sample_spec),
|
||||
pa_channel_map_snprint(cm, sizeof(cm), &s->channel_map));
|
||||
|
||||
return s;
|
||||
}
|
||||
|
|
@ -134,15 +227,16 @@ static int source_set_state(pa_source *s, pa_source_state_t state) {
|
|||
return 0;
|
||||
|
||||
suspend_change =
|
||||
(s->state == PA_SOURCE_SUSPENDED && PA_SOURCE_OPENED(state)) ||
|
||||
(PA_SOURCE_OPENED(s->state) && state == PA_SOURCE_SUSPENDED);
|
||||
(s->state == PA_SOURCE_SUSPENDED && PA_SOURCE_IS_OPENED(state)) ||
|
||||
(PA_SOURCE_IS_OPENED(s->state) && state == PA_SOURCE_SUSPENDED);
|
||||
|
||||
if (s->set_state)
|
||||
if ((ret = s->set_state(s, state)) < 0)
|
||||
return -1;
|
||||
|
||||
if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) < 0)
|
||||
return -1;
|
||||
if (s->asyncmsgq)
|
||||
if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) < 0)
|
||||
return -1;
|
||||
|
||||
s->state = state;
|
||||
|
||||
|
|
@ -167,13 +261,18 @@ void pa_source_put(pa_source *s) {
|
|||
pa_source_assert_ref(s);
|
||||
|
||||
pa_assert(s->state == PA_SINK_INIT);
|
||||
pa_assert(s->rtpoll);
|
||||
pa_assert(s->asyncmsgq);
|
||||
pa_assert(s->rtpoll);
|
||||
|
||||
pa_assert(!s->min_latency || !s->max_latency || s->min_latency <= s->max_latency);
|
||||
|
||||
if (!(s->flags & PA_SOURCE_HW_VOLUME_CTRL))
|
||||
s->flags |= PA_SOURCE_DECIBEL_VOLUME;
|
||||
|
||||
pa_assert_se(source_set_state(s, PA_SOURCE_IDLE) == 0);
|
||||
|
||||
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_NEW, s->index);
|
||||
pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_NEW_POST], s);
|
||||
pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_PUT], s);
|
||||
}
|
||||
|
||||
void pa_source_unlink(pa_source *s) {
|
||||
|
|
@ -185,7 +284,7 @@ void pa_source_unlink(pa_source *s) {
|
|||
/* See pa_sink_unlink() for a couple of comments how this function
|
||||
* works. */
|
||||
|
||||
linked = PA_SOURCE_LINKED(s->state);
|
||||
linked = PA_SOURCE_IS_LINKED(s->state);
|
||||
|
||||
if (linked)
|
||||
pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], s);
|
||||
|
|
@ -205,12 +304,7 @@ void pa_source_unlink(pa_source *s) {
|
|||
else
|
||||
s->state = PA_SOURCE_UNLINKED;
|
||||
|
||||
s->get_latency = NULL;
|
||||
s->get_volume = NULL;
|
||||
s->set_volume = NULL;
|
||||
s->set_mute = NULL;
|
||||
s->get_mute = NULL;
|
||||
s->set_state = NULL;
|
||||
reset_callbacks(s);
|
||||
|
||||
if (linked) {
|
||||
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_REMOVE, s->index);
|
||||
|
|
@ -225,7 +319,7 @@ static void source_free(pa_object *o) {
|
|||
pa_assert(s);
|
||||
pa_assert(pa_source_refcnt(s) == 0);
|
||||
|
||||
if (PA_SOURCE_LINKED(s->state))
|
||||
if (PA_SOURCE_IS_LINKED(s->state))
|
||||
pa_source_unlink(s);
|
||||
|
||||
pa_log_info("Freeing source %u \"%s\"", s->index, s->name);
|
||||
|
|
@ -237,15 +331,33 @@ static void source_free(pa_object *o) {
|
|||
|
||||
pa_hashmap_free(s->thread_info.outputs, NULL, NULL);
|
||||
|
||||
if (s->silence.memblock)
|
||||
pa_memblock_unref(s->silence.memblock);
|
||||
|
||||
pa_xfree(s->name);
|
||||
pa_xfree(s->description);
|
||||
pa_xfree(s->driver);
|
||||
|
||||
if (s->proplist)
|
||||
pa_proplist_free(s->proplist);
|
||||
|
||||
pa_xfree(s);
|
||||
}
|
||||
|
||||
void pa_source_set_asyncmsgq(pa_source *s, pa_asyncmsgq *q) {
|
||||
pa_source_assert_ref(s);
|
||||
|
||||
s->asyncmsgq = q;
|
||||
}
|
||||
|
||||
void pa_source_set_rtpoll(pa_source *s, pa_rtpoll *p) {
|
||||
pa_source_assert_ref(s);
|
||||
|
||||
s->rtpoll = p;
|
||||
}
|
||||
|
||||
int pa_source_update_status(pa_source*s) {
|
||||
pa_source_assert_ref(s);
|
||||
pa_assert(PA_SOURCE_LINKED(s->state));
|
||||
pa_assert(PA_SOURCE_IS_LINKED(s->state));
|
||||
|
||||
if (s->state == PA_SOURCE_SUSPENDED)
|
||||
return 0;
|
||||
|
|
@ -255,7 +367,7 @@ int pa_source_update_status(pa_source*s) {
|
|||
|
||||
int pa_source_suspend(pa_source *s, pa_bool_t suspend) {
|
||||
pa_source_assert_ref(s);
|
||||
pa_assert(PA_SOURCE_LINKED(s->state));
|
||||
pa_assert(PA_SOURCE_IS_LINKED(s->state));
|
||||
|
||||
if (suspend)
|
||||
return source_set_state(s, PA_SOURCE_SUSPENDED);
|
||||
|
|
@ -263,11 +375,22 @@ int pa_source_suspend(pa_source *s, pa_bool_t suspend) {
|
|||
return source_set_state(s, pa_source_used_by(s) ? PA_SOURCE_RUNNING : PA_SOURCE_IDLE);
|
||||
}
|
||||
|
||||
void pa_source_ping(pa_source *s) {
|
||||
pa_source_assert_ref(s);
|
||||
pa_assert(PA_SOURCE_LINKED(s->state));
|
||||
void pa_source_process_rewind(pa_source *s, size_t nbytes) {
|
||||
pa_source_output *o;
|
||||
void *state = NULL;
|
||||
|
||||
pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_PING, NULL, 0, NULL, NULL);
|
||||
pa_source_assert_ref(s);
|
||||
pa_assert(PA_SOURCE_IS_OPENED(s->thread_info.state));
|
||||
|
||||
if (nbytes <= 0)
|
||||
return;
|
||||
|
||||
pa_log_debug("Processing rewind...");
|
||||
|
||||
while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) {
|
||||
pa_source_output_assert_ref(o);
|
||||
pa_source_output_process_rewind(o, nbytes);
|
||||
}
|
||||
}
|
||||
|
||||
void pa_source_post(pa_source*s, const pa_memchunk *chunk) {
|
||||
|
|
@ -275,7 +398,7 @@ void pa_source_post(pa_source*s, const pa_memchunk *chunk) {
|
|||
void *state = NULL;
|
||||
|
||||
pa_source_assert_ref(s);
|
||||
pa_assert(PA_SOURCE_OPENED(s->thread_info.state));
|
||||
pa_assert(PA_SOURCE_IS_OPENED(s->thread_info.state));
|
||||
pa_assert(chunk);
|
||||
|
||||
if (s->thread_info.state != PA_SOURCE_RUNNING)
|
||||
|
|
@ -292,14 +415,18 @@ void pa_source_post(pa_source*s, const pa_memchunk *chunk) {
|
|||
else
|
||||
pa_volume_memchunk(&vchunk, &s->sample_spec, &s->thread_info.soft_volume);
|
||||
|
||||
while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
|
||||
while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) {
|
||||
pa_source_output_assert_ref(o);
|
||||
pa_source_output_push(o, &vchunk);
|
||||
}
|
||||
|
||||
pa_memblock_unref(vchunk.memblock);
|
||||
} else {
|
||||
|
||||
while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
|
||||
while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) {
|
||||
pa_source_output_assert_ref(o);
|
||||
pa_source_output_push(o, chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -307,14 +434,11 @@ pa_usec_t pa_source_get_latency(pa_source *s) {
|
|||
pa_usec_t usec;
|
||||
|
||||
pa_source_assert_ref(s);
|
||||
pa_assert(PA_SOURCE_LINKED(s->state));
|
||||
pa_assert(PA_SOURCE_IS_LINKED(s->state));
|
||||
|
||||
if (!PA_SOURCE_OPENED(s->state))
|
||||
if (!PA_SOURCE_IS_OPENED(s->state))
|
||||
return 0;
|
||||
|
||||
if (s->get_latency)
|
||||
return s->get_latency(s);
|
||||
|
||||
if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
|
||||
return 0;
|
||||
|
||||
|
|
@ -325,7 +449,7 @@ void pa_source_set_volume(pa_source *s, const pa_cvolume *volume) {
|
|||
int changed;
|
||||
|
||||
pa_source_assert_ref(s);
|
||||
pa_assert(PA_SOURCE_LINKED(s->state));
|
||||
pa_assert(PA_SOURCE_IS_LINKED(s->state));
|
||||
pa_assert(volume);
|
||||
|
||||
changed = !pa_cvolume_equal(volume, &s->volume);
|
||||
|
|
@ -345,7 +469,7 @@ const pa_cvolume *pa_source_get_volume(pa_source *s) {
|
|||
pa_cvolume old_volume;
|
||||
|
||||
pa_source_assert_ref(s);
|
||||
pa_assert(PA_SOURCE_LINKED(s->state));
|
||||
pa_assert(PA_SOURCE_IS_LINKED(s->state));
|
||||
|
||||
old_volume = s->volume;
|
||||
|
||||
|
|
@ -365,7 +489,7 @@ void pa_source_set_mute(pa_source *s, pa_bool_t mute) {
|
|||
int changed;
|
||||
|
||||
pa_source_assert_ref(s);
|
||||
pa_assert(PA_SOURCE_LINKED(s->state));
|
||||
pa_assert(PA_SOURCE_IS_LINKED(s->state));
|
||||
|
||||
changed = s->muted != mute;
|
||||
s->muted = mute;
|
||||
|
|
@ -384,7 +508,7 @@ pa_bool_t pa_source_get_mute(pa_source *s) {
|
|||
pa_bool_t old_muted;
|
||||
|
||||
pa_source_assert_ref(s);
|
||||
pa_assert(PA_SOURCE_LINKED(s->state));
|
||||
pa_assert(PA_SOURCE_IS_LINKED(s->state));
|
||||
|
||||
old_muted = s->muted;
|
||||
|
||||
|
|
@ -400,52 +524,32 @@ pa_bool_t pa_source_get_mute(pa_source *s) {
|
|||
return s->muted;
|
||||
}
|
||||
|
||||
void pa_source_set_module(pa_source *s, pa_module *m) {
|
||||
pa_source_assert_ref(s);
|
||||
|
||||
if (m == s->module)
|
||||
return;
|
||||
|
||||
s->module = m;
|
||||
|
||||
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
|
||||
}
|
||||
|
||||
void pa_source_set_description(pa_source *s, const char *description) {
|
||||
const char *old;
|
||||
pa_source_assert_ref(s);
|
||||
|
||||
if (!description && !s->description)
|
||||
if (!description && !pa_proplist_contains(s->proplist, PA_PROP_DEVICE_DESCRIPTION))
|
||||
return;
|
||||
|
||||
if (description && s->description && !strcmp(description, s->description))
|
||||
old = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
|
||||
|
||||
if (old && description && !strcmp(old, description))
|
||||
return;
|
||||
|
||||
pa_xfree(s->description);
|
||||
s->description = pa_xstrdup(description);
|
||||
if (description)
|
||||
pa_proplist_sets(s->proplist, PA_PROP_DEVICE_DESCRIPTION, description);
|
||||
else
|
||||
pa_proplist_unset(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
|
||||
|
||||
if (PA_SOURCE_LINKED(s->state)) {
|
||||
pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_DESCRIPTION_CHANGED], s);
|
||||
if (PA_SOURCE_IS_LINKED(s->state)) {
|
||||
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
|
||||
pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED], s);
|
||||
}
|
||||
}
|
||||
|
||||
void pa_source_set_asyncmsgq(pa_source *s, pa_asyncmsgq *q) {
|
||||
pa_source_assert_ref(s);
|
||||
pa_assert(q);
|
||||
|
||||
s->asyncmsgq = q;
|
||||
}
|
||||
|
||||
void pa_source_set_rtpoll(pa_source *s, pa_rtpoll *p) {
|
||||
pa_source_assert_ref(s);
|
||||
pa_assert(p);
|
||||
|
||||
s->rtpoll = p;
|
||||
}
|
||||
|
||||
unsigned pa_source_linked_by(pa_source *s) {
|
||||
pa_source_assert_ref(s);
|
||||
pa_assert(PA_SOURCE_LINKED(s->state));
|
||||
pa_assert(PA_SOURCE_IS_LINKED(s->state));
|
||||
|
||||
return pa_idxset_size(s->outputs);
|
||||
}
|
||||
|
|
@ -454,7 +558,7 @@ unsigned pa_source_used_by(pa_source *s) {
|
|||
unsigned ret;
|
||||
|
||||
pa_source_assert_ref(s);
|
||||
pa_assert(PA_SOURCE_LINKED(s->state));
|
||||
pa_assert(PA_SOURCE_IS_LINKED(s->state));
|
||||
|
||||
ret = pa_idxset_size(s->outputs);
|
||||
pa_assert(ret >= s->n_corked);
|
||||
|
|
@ -470,6 +574,7 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_
|
|||
switch ((pa_source_message_t) code) {
|
||||
case PA_SOURCE_MESSAGE_ADD_OUTPUT: {
|
||||
pa_source_output *o = PA_SOURCE_OUTPUT(userdata);
|
||||
|
||||
pa_hashmap_put(s->thread_info.outputs, PA_UINT32_TO_PTR(o->index), pa_source_output_ref(o));
|
||||
|
||||
pa_assert(!o->thread_info.attached);
|
||||
|
|
@ -478,12 +583,23 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_
|
|||
if (o->attach)
|
||||
o->attach(o);
|
||||
|
||||
pa_source_output_set_state_within_thread(o, o->state);
|
||||
|
||||
pa_source_output_update_max_rewind(o, s->thread_info.max_rewind);
|
||||
|
||||
/* We don't just invalidate the requested latency here,
|
||||
* because if we are in a move we might need to fix up the
|
||||
* requested latency. */
|
||||
pa_source_output_set_requested_latency_within_thread(o, o->thread_info.requested_source_latency);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
case PA_SOURCE_MESSAGE_REMOVE_OUTPUT: {
|
||||
pa_source_output *o = PA_SOURCE_OUTPUT(userdata);
|
||||
|
||||
pa_source_output_set_state_within_thread(o, o->state);
|
||||
|
||||
if (o->detach)
|
||||
o->detach(o);
|
||||
|
||||
|
|
@ -493,6 +609,8 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_
|
|||
if (pa_hashmap_remove(s->thread_info.outputs, PA_UINT32_TO_PTR(o->index)))
|
||||
pa_source_output_unref(o);
|
||||
|
||||
pa_source_invalidate_requested_latency(s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -512,9 +630,6 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_
|
|||
*((pa_bool_t*) userdata) = s->thread_info.soft_muted;
|
||||
return 0;
|
||||
|
||||
case PA_SOURCE_MESSAGE_PING:
|
||||
return 0;
|
||||
|
||||
case PA_SOURCE_MESSAGE_SET_STATE:
|
||||
s->thread_info.state = PA_PTR_TO_UINT(userdata);
|
||||
return 0;
|
||||
|
|
@ -525,13 +640,20 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_
|
|||
* asyncmsgq and rtpoll fields can be changed without
|
||||
* problems */
|
||||
pa_source_detach_within_thread(s);
|
||||
break;
|
||||
return 0;
|
||||
|
||||
case PA_SOURCE_MESSAGE_ATTACH:
|
||||
|
||||
/* Reattach all streams */
|
||||
pa_source_attach_within_thread(s);
|
||||
break;
|
||||
return 0;
|
||||
|
||||
case PA_SOURCE_MESSAGE_GET_REQUESTED_LATENCY: {
|
||||
|
||||
pa_usec_t *usec = userdata;
|
||||
*usec = pa_source_get_requested_latency_within_thread(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
case PA_SOURCE_MESSAGE_GET_LATENCY:
|
||||
case PA_SOURCE_MESSAGE_MAX:
|
||||
|
|
@ -556,14 +678,14 @@ int pa_source_suspend_all(pa_core *c, pa_bool_t suspend) {
|
|||
|
||||
void pa_source_detach(pa_source *s) {
|
||||
pa_source_assert_ref(s);
|
||||
pa_assert(PA_SOURCE_LINKED(s->state));
|
||||
pa_assert(PA_SOURCE_IS_LINKED(s->state));
|
||||
|
||||
pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_DETACH, NULL, 0, NULL);
|
||||
}
|
||||
|
||||
void pa_source_attach(pa_source *s) {
|
||||
pa_source_assert_ref(s);
|
||||
pa_assert(PA_SOURCE_LINKED(s->state));
|
||||
pa_assert(PA_SOURCE_IS_LINKED(s->state));
|
||||
|
||||
pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_ATTACH, NULL, 0, NULL);
|
||||
}
|
||||
|
|
@ -573,7 +695,7 @@ void pa_source_detach_within_thread(pa_source *s) {
|
|||
void *state = NULL;
|
||||
|
||||
pa_source_assert_ref(s);
|
||||
pa_assert(PA_SOURCE_LINKED(s->thread_info.state));
|
||||
pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state));
|
||||
|
||||
while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
|
||||
if (o->detach)
|
||||
|
|
@ -585,10 +707,83 @@ void pa_source_attach_within_thread(pa_source *s) {
|
|||
void *state = NULL;
|
||||
|
||||
pa_source_assert_ref(s);
|
||||
pa_assert(PA_SOURCE_LINKED(s->thread_info.state));
|
||||
pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state));
|
||||
|
||||
while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
|
||||
if (o->attach)
|
||||
o->attach(o);
|
||||
|
||||
}
|
||||
|
||||
pa_usec_t pa_source_get_requested_latency_within_thread(pa_source *s) {
|
||||
pa_usec_t result = (pa_usec_t) -1;
|
||||
pa_source_output *o;
|
||||
void *state = NULL;
|
||||
|
||||
pa_source_assert_ref(s);
|
||||
|
||||
if (s->thread_info.requested_latency_valid)
|
||||
return s->thread_info.requested_latency;
|
||||
|
||||
while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
|
||||
|
||||
if (o->thread_info.requested_source_latency != (pa_usec_t) -1 &&
|
||||
(result == (pa_usec_t) -1 || result > o->thread_info.requested_source_latency))
|
||||
result = o->thread_info.requested_source_latency;
|
||||
|
||||
if (result != (pa_usec_t) -1) {
|
||||
if (s->max_latency > 0 && result > s->max_latency)
|
||||
result = s->max_latency;
|
||||
|
||||
if (s->min_latency > 0 && result < s->min_latency)
|
||||
result = s->min_latency;
|
||||
}
|
||||
|
||||
s->thread_info.requested_latency = result;
|
||||
s->thread_info.requested_latency_valid = TRUE;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
pa_usec_t pa_source_get_requested_latency(pa_source *s) {
|
||||
pa_usec_t usec;
|
||||
|
||||
pa_source_assert_ref(s);
|
||||
pa_assert(PA_SOURCE_IS_LINKED(s->state));
|
||||
|
||||
if (!PA_SOURCE_IS_OPENED(s->state))
|
||||
return 0;
|
||||
|
||||
if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) < 0)
|
||||
return 0;
|
||||
|
||||
if (usec == (pa_usec_t) -1)
|
||||
usec = s->max_latency;
|
||||
|
||||
return usec;
|
||||
}
|
||||
|
||||
void pa_source_set_max_rewind(pa_source *s, size_t max_rewind) {
|
||||
pa_source_output *o;
|
||||
void *state = NULL;
|
||||
|
||||
pa_source_assert_ref(s);
|
||||
|
||||
if (max_rewind == s->thread_info.max_rewind)
|
||||
return;
|
||||
|
||||
s->thread_info.max_rewind = max_rewind;
|
||||
|
||||
while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
|
||||
pa_source_output_update_max_rewind(o, s->thread_info.max_rewind);
|
||||
}
|
||||
|
||||
void pa_source_invalidate_requested_latency(pa_source *s) {
|
||||
|
||||
pa_source_assert_ref(s);
|
||||
pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state));
|
||||
|
||||
s->thread_info.requested_latency_valid = FALSE;
|
||||
|
||||
if (s->update_requested_latency)
|
||||
s->update_requested_latency(s);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ typedef struct pa_source pa_source;
|
|||
#include <pulse/channelmap.h>
|
||||
#include <pulse/volume.h>
|
||||
|
||||
#include <pulsecore/core-def.h>
|
||||
#include <pulsecore/core.h>
|
||||
#include <pulsecore/idxset.h>
|
||||
#include <pulsecore/memblock.h>
|
||||
|
|
@ -54,11 +53,11 @@ typedef enum pa_source_state {
|
|||
PA_SOURCE_UNLINKED
|
||||
} pa_source_state_t;
|
||||
|
||||
static inline pa_bool_t PA_SOURCE_OPENED(pa_source_state_t x) {
|
||||
static inline pa_bool_t PA_SOURCE_IS_OPENED(pa_source_state_t x) {
|
||||
return x == PA_SOURCE_RUNNING || x == PA_SOURCE_IDLE;
|
||||
}
|
||||
|
||||
static inline pa_bool_t PA_SOURCE_LINKED(pa_source_state_t x) {
|
||||
static inline pa_bool_t PA_SOURCE_IS_LINKED(pa_source_state_t x) {
|
||||
return x == PA_SOURCE_RUNNING || x == PA_SOURCE_IDLE || x == PA_SOURCE_SUSPENDED;
|
||||
}
|
||||
|
||||
|
|
@ -71,7 +70,8 @@ struct pa_source {
|
|||
pa_source_flags_t flags;
|
||||
|
||||
char *name;
|
||||
char *description, *driver; /* may be NULL */
|
||||
char *driver; /* may be NULL */
|
||||
pa_proplist *proplist;
|
||||
|
||||
pa_module *module; /* may be NULL */
|
||||
|
||||
|
|
@ -87,15 +87,20 @@ struct pa_source {
|
|||
pa_bool_t refresh_volume;
|
||||
pa_bool_t refresh_muted;
|
||||
|
||||
pa_asyncmsgq *asyncmsgq;
|
||||
pa_rtpoll *rtpoll;
|
||||
|
||||
pa_memchunk silence;
|
||||
|
||||
pa_usec_t min_latency; /* we won't go below this latency setting */
|
||||
pa_usec_t max_latency; /* An upper limit for the latencies */
|
||||
|
||||
int (*set_state)(pa_source*source, pa_source_state_t state); /* may be NULL */
|
||||
int (*set_volume)(pa_source *s); /* dito */
|
||||
int (*get_volume)(pa_source *s); /* dito */
|
||||
int (*set_mute)(pa_source *s); /* dito */
|
||||
int (*get_mute)(pa_source *s); /* dito */
|
||||
pa_usec_t (*get_latency)(pa_source *s); /* dito */
|
||||
|
||||
pa_asyncmsgq *asyncmsgq;
|
||||
pa_rtpoll *rtpoll;
|
||||
void (*update_requested_latency)(pa_source *s); /* dito */
|
||||
|
||||
/* Contains copies of the above data so that the real-time worker
|
||||
* thread can work without access locking */
|
||||
|
|
@ -104,6 +109,13 @@ struct pa_source {
|
|||
pa_hashmap *outputs;
|
||||
pa_cvolume soft_volume;
|
||||
pa_bool_t soft_muted;
|
||||
|
||||
pa_bool_t requested_latency_valid;
|
||||
size_t requested_latency;
|
||||
|
||||
/* Then number of bytes this source will be rewound for at
|
||||
* max */
|
||||
size_t max_rewind;
|
||||
} thread_info;
|
||||
|
||||
void *userdata;
|
||||
|
|
@ -120,27 +132,50 @@ typedef enum pa_source_message {
|
|||
PA_SOURCE_MESSAGE_GET_MUTE,
|
||||
PA_SOURCE_MESSAGE_SET_MUTE,
|
||||
PA_SOURCE_MESSAGE_GET_LATENCY,
|
||||
PA_SOURCE_MESSAGE_GET_REQUESTED_LATENCY,
|
||||
PA_SOURCE_MESSAGE_SET_STATE,
|
||||
PA_SOURCE_MESSAGE_PING,
|
||||
PA_SOURCE_MESSAGE_ATTACH,
|
||||
PA_SOURCE_MESSAGE_DETACH,
|
||||
PA_SOURCE_MESSAGE_MAX
|
||||
} pa_source_message_t;
|
||||
|
||||
typedef struct pa_source_new_data {
|
||||
char *name;
|
||||
pa_bool_t namereg_fail;
|
||||
pa_proplist *proplist;
|
||||
|
||||
const char *driver;
|
||||
pa_module *module;
|
||||
|
||||
pa_sample_spec sample_spec;
|
||||
pa_bool_t sample_spec_is_set;
|
||||
pa_channel_map channel_map;
|
||||
pa_bool_t channel_map_is_set;
|
||||
|
||||
pa_cvolume volume;
|
||||
pa_bool_t volume_is_set;
|
||||
pa_bool_t muted;
|
||||
pa_bool_t muted_is_set;
|
||||
} pa_source_new_data;
|
||||
|
||||
pa_source_new_data* pa_source_new_data_init(pa_source_new_data *data);
|
||||
void pa_source_new_data_set_name(pa_source_new_data *data, const char *name);
|
||||
void pa_source_new_data_set_sample_spec(pa_source_new_data *data, const pa_sample_spec *spec);
|
||||
void pa_source_new_data_set_channel_map(pa_source_new_data *data, const pa_channel_map *map);
|
||||
void pa_source_new_data_set_volume(pa_source_new_data *data, const pa_cvolume *volume);
|
||||
void pa_source_new_data_set_muted(pa_source_new_data *data, pa_bool_t mute);
|
||||
void pa_source_new_data_done(pa_source_new_data *data);
|
||||
|
||||
/* To be called exclusively by the source driver, from main context */
|
||||
|
||||
pa_source* pa_source_new(
|
||||
pa_core *core,
|
||||
const char *driver,
|
||||
const char *name,
|
||||
int namereg_fail,
|
||||
const pa_sample_spec *spec,
|
||||
const pa_channel_map *map);
|
||||
pa_source_new_data *data,
|
||||
pa_source_flags_t flags);
|
||||
|
||||
void pa_source_put(pa_source *s);
|
||||
void pa_source_unlink(pa_source *s);
|
||||
|
||||
void pa_source_set_module(pa_source *s, pa_module *m);
|
||||
void pa_source_set_description(pa_source *s, const char *description);
|
||||
void pa_source_set_asyncmsgq(pa_source *s, pa_asyncmsgq *q);
|
||||
void pa_source_set_rtpoll(pa_source *s, pa_rtpoll *p);
|
||||
|
|
@ -151,13 +186,12 @@ void pa_source_attach(pa_source *s);
|
|||
/* May be called by everyone, from main context */
|
||||
|
||||
pa_usec_t pa_source_get_latency(pa_source *s);
|
||||
pa_usec_t pa_source_get_requested_latency(pa_source *s);
|
||||
|
||||
int pa_source_update_status(pa_source*s);
|
||||
int pa_source_suspend(pa_source *s, pa_bool_t suspend);
|
||||
int pa_source_suspend_all(pa_core *c, pa_bool_t suspend);
|
||||
|
||||
void pa_source_ping(pa_source *s);
|
||||
|
||||
void pa_source_set_volume(pa_source *source, const pa_cvolume *volume);
|
||||
const pa_cvolume *pa_source_get_volume(pa_source *source);
|
||||
void pa_source_set_mute(pa_source *source, pa_bool_t mute);
|
||||
|
|
@ -170,10 +204,19 @@ unsigned pa_source_used_by(pa_source *s); /* Number of connected streams that ar
|
|||
/* To be called exclusively by the source driver, from IO context */
|
||||
|
||||
void pa_source_post(pa_source*s, const pa_memchunk *b);
|
||||
void pa_source_process_rewind(pa_source *s, size_t nbytes);
|
||||
|
||||
int pa_source_process_msg(pa_msgobject *o, int code, void *userdata, int64_t, pa_memchunk *chunk);
|
||||
|
||||
void pa_source_attach_within_thread(pa_source *s);
|
||||
void pa_source_detach_within_thread(pa_source *s);
|
||||
|
||||
pa_usec_t pa_source_get_requested_latency_within_thread(pa_source *s);
|
||||
|
||||
void pa_source_set_max_rewind(pa_source *s, size_t max_rewind);
|
||||
|
||||
/* To be called exclusively by source output drivers, from IO context */
|
||||
|
||||
void pa_source_invalidate_requested_latency(pa_source *s);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
USA.
|
||||
***/
|
||||
|
||||
#include <pulsecore/gccmacro.h>
|
||||
#include <pulse/gccmacro.h>
|
||||
|
||||
typedef struct pa_strbuf pa_strbuf;
|
||||
|
||||
|
|
|
|||
|
|
@ -42,12 +42,14 @@
|
|||
|
||||
#include "tagstruct.h"
|
||||
|
||||
#define MAX_TAG_SIZE (64*1024)
|
||||
|
||||
struct pa_tagstruct {
|
||||
uint8_t *data;
|
||||
size_t length, allocated;
|
||||
size_t rindex;
|
||||
|
||||
int dynamic;
|
||||
pa_bool_t dynamic;
|
||||
};
|
||||
|
||||
pa_tagstruct *pa_tagstruct_new(const uint8_t* data, size_t length) {
|
||||
|
|
@ -161,7 +163,7 @@ void pa_tagstruct_put_arbitrary(pa_tagstruct *t, const void *p, size_t length) {
|
|||
t->length += 5+length;
|
||||
}
|
||||
|
||||
void pa_tagstruct_put_boolean(pa_tagstruct*t, int b) {
|
||||
void pa_tagstruct_put_boolean(pa_tagstruct*t, pa_bool_t b) {
|
||||
pa_assert(t);
|
||||
|
||||
extend(t, 1);
|
||||
|
|
@ -254,6 +256,32 @@ void pa_tagstruct_put_cvolume(pa_tagstruct *t, const pa_cvolume *cvolume) {
|
|||
}
|
||||
}
|
||||
|
||||
void pa_tagstruct_put_proplist(pa_tagstruct *t, pa_proplist *p) {
|
||||
void *state = NULL;
|
||||
pa_assert(t);
|
||||
pa_assert(p);
|
||||
|
||||
extend(t, 1);
|
||||
|
||||
t->data[t->length++] = PA_TAG_PROPLIST;
|
||||
|
||||
for (;;) {
|
||||
const char *k;
|
||||
const void *d;
|
||||
size_t l;
|
||||
|
||||
if (!(k = pa_proplist_iterate(p, &state)))
|
||||
break;
|
||||
|
||||
pa_tagstruct_puts(t, k);
|
||||
pa_assert_se(pa_proplist_get(p, k, &d, &l) >= 0);
|
||||
pa_tagstruct_putu32(t, (uint32_t) l);
|
||||
pa_tagstruct_put_arbitrary(t, d, l);
|
||||
}
|
||||
|
||||
pa_tagstruct_puts(t, NULL);
|
||||
}
|
||||
|
||||
int pa_tagstruct_gets(pa_tagstruct*t, const char **s) {
|
||||
int error = 0;
|
||||
size_t n;
|
||||
|
|
@ -379,7 +407,7 @@ const uint8_t* pa_tagstruct_data(pa_tagstruct*t, size_t *l) {
|
|||
return t->data;
|
||||
}
|
||||
|
||||
int pa_tagstruct_get_boolean(pa_tagstruct*t, int *b) {
|
||||
int pa_tagstruct_get_boolean(pa_tagstruct*t, pa_bool_t *b) {
|
||||
pa_assert(t);
|
||||
pa_assert(b);
|
||||
|
||||
|
|
@ -387,9 +415,9 @@ int pa_tagstruct_get_boolean(pa_tagstruct*t, int *b) {
|
|||
return -1;
|
||||
|
||||
if (t->data[t->rindex] == PA_TAG_BOOLEAN_TRUE)
|
||||
*b = 1;
|
||||
*b = TRUE;
|
||||
else if (t->data[t->rindex] == PA_TAG_BOOLEAN_FALSE)
|
||||
*b = 0;
|
||||
*b = FALSE;
|
||||
else
|
||||
return -1;
|
||||
|
||||
|
|
@ -529,6 +557,52 @@ int pa_tagstruct_get_cvolume(pa_tagstruct *t, pa_cvolume *cvolume) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int pa_tagstruct_get_proplist(pa_tagstruct *t, pa_proplist *p) {
|
||||
size_t saved_rindex;
|
||||
|
||||
pa_assert(t);
|
||||
pa_assert(p);
|
||||
|
||||
if (t->rindex+1 > t->length)
|
||||
return -1;
|
||||
|
||||
if (t->data[t->rindex] != PA_TAG_PROPLIST)
|
||||
return -1;
|
||||
|
||||
saved_rindex = t->rindex;
|
||||
t->rindex++;
|
||||
|
||||
for (;;) {
|
||||
const char *k;
|
||||
const void *d;
|
||||
uint32_t length;
|
||||
|
||||
if (pa_tagstruct_gets(t, &k) < 0)
|
||||
goto fail;
|
||||
|
||||
if (!k)
|
||||
break;
|
||||
|
||||
if (pa_tagstruct_getu32(t, &length) < 0)
|
||||
goto fail;
|
||||
|
||||
if (length > MAX_TAG_SIZE)
|
||||
goto fail;
|
||||
|
||||
if (pa_tagstruct_get_arbitrary(t, &d, length) < 0)
|
||||
goto fail;
|
||||
|
||||
if (pa_proplist_set(p, k, d, length) < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
t->rindex = saved_rindex;
|
||||
return -1;
|
||||
}
|
||||
|
||||
void pa_tagstruct_put(pa_tagstruct *t, ...) {
|
||||
va_list va;
|
||||
pa_assert(t);
|
||||
|
|
@ -591,6 +665,10 @@ void pa_tagstruct_put(pa_tagstruct *t, ...) {
|
|||
pa_tagstruct_put_cvolume(t, va_arg(va, pa_cvolume *));
|
||||
break;
|
||||
|
||||
case PA_TAG_PROPLIST:
|
||||
pa_tagstruct_put_proplist(t, va_arg(va, pa_proplist *));
|
||||
break;
|
||||
|
||||
default:
|
||||
pa_assert_not_reached();
|
||||
}
|
||||
|
|
@ -643,7 +721,7 @@ int pa_tagstruct_get(pa_tagstruct *t, ...) {
|
|||
|
||||
case PA_TAG_BOOLEAN_TRUE:
|
||||
case PA_TAG_BOOLEAN_FALSE:
|
||||
ret = pa_tagstruct_get_boolean(t, va_arg(va, int*));
|
||||
ret = pa_tagstruct_get_boolean(t, va_arg(va, pa_bool_t*));
|
||||
break;
|
||||
|
||||
case PA_TAG_TIMEVAL:
|
||||
|
|
@ -662,6 +740,10 @@ int pa_tagstruct_get(pa_tagstruct *t, ...) {
|
|||
ret = pa_tagstruct_get_cvolume(t, va_arg(va, pa_cvolume *));
|
||||
break;
|
||||
|
||||
case PA_TAG_PROPLIST:
|
||||
ret = pa_tagstruct_get_proplist(t, va_arg(va, pa_proplist *));
|
||||
break;
|
||||
|
||||
default:
|
||||
pa_assert_not_reached();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,10 @@
|
|||
#include <pulse/sample.h>
|
||||
#include <pulse/channelmap.h>
|
||||
#include <pulse/volume.h>
|
||||
#include <pulse/proplist.h>
|
||||
#include <pulse/gccmacro.h>
|
||||
|
||||
#include <pulsecore/macro.h>
|
||||
|
||||
typedef struct pa_tagstruct pa_tagstruct;
|
||||
|
||||
|
|
@ -51,7 +55,8 @@ enum {
|
|||
PA_TAG_TIMEVAL = 'T',
|
||||
PA_TAG_USEC = 'U' /* 64bit unsigned */,
|
||||
PA_TAG_CHANNEL_MAP = 'm',
|
||||
PA_TAG_CVOLUME = 'v'
|
||||
PA_TAG_CVOLUME = 'v',
|
||||
PA_TAG_PROPLIST = 'P'
|
||||
};
|
||||
|
||||
pa_tagstruct *pa_tagstruct_new(const uint8_t* data, size_t length);
|
||||
|
|
@ -70,11 +75,12 @@ void pa_tagstruct_putu64(pa_tagstruct*t, uint64_t i);
|
|||
void pa_tagstruct_puts64(pa_tagstruct*t, int64_t i);
|
||||
void pa_tagstruct_put_sample_spec(pa_tagstruct *t, const pa_sample_spec *ss);
|
||||
void pa_tagstruct_put_arbitrary(pa_tagstruct*t, const void *p, size_t length);
|
||||
void pa_tagstruct_put_boolean(pa_tagstruct*t, int b);
|
||||
void pa_tagstruct_put_boolean(pa_tagstruct*t, pa_bool_t b);
|
||||
void pa_tagstruct_put_timeval(pa_tagstruct*t, const struct timeval *tv);
|
||||
void pa_tagstruct_put_usec(pa_tagstruct*t, pa_usec_t u);
|
||||
void pa_tagstruct_put_channel_map(pa_tagstruct *t, const pa_channel_map *map);
|
||||
void pa_tagstruct_put_cvolume(pa_tagstruct *t, const pa_cvolume *cvolume);
|
||||
void pa_tagstruct_put_proplist(pa_tagstruct *t, pa_proplist *p);
|
||||
|
||||
int pa_tagstruct_get(pa_tagstruct *t, ...);
|
||||
|
||||
|
|
@ -85,11 +91,12 @@ int pa_tagstruct_getu64(pa_tagstruct*t, uint64_t *i);
|
|||
int pa_tagstruct_gets64(pa_tagstruct*t, int64_t *i);
|
||||
int pa_tagstruct_get_sample_spec(pa_tagstruct *t, pa_sample_spec *ss);
|
||||
int pa_tagstruct_get_arbitrary(pa_tagstruct *t, const void **p, size_t length);
|
||||
int pa_tagstruct_get_boolean(pa_tagstruct *t, int *b);
|
||||
int pa_tagstruct_get_boolean(pa_tagstruct *t, pa_bool_t *b);
|
||||
int pa_tagstruct_get_timeval(pa_tagstruct*t, struct timeval *tv);
|
||||
int pa_tagstruct_get_usec(pa_tagstruct*t, pa_usec_t *u);
|
||||
int pa_tagstruct_get_channel_map(pa_tagstruct *t, pa_channel_map *map);
|
||||
int pa_tagstruct_get_cvolume(pa_tagstruct *t, pa_cvolume *v);
|
||||
int pa_tagstruct_get_proplist(pa_tagstruct *t, pa_proplist *p);
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -43,15 +43,15 @@
|
|||
|
||||
PA_STATIC_TLS_DECLARE_NO_FREE(thread_mq);
|
||||
|
||||
static void asyncmsgq_cb(pa_mainloop_api*api, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata) {
|
||||
static void asyncmsgq_read_cb(pa_mainloop_api*api, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata) {
|
||||
pa_thread_mq *q = userdata;
|
||||
pa_asyncmsgq *aq;
|
||||
|
||||
pa_assert(pa_asyncmsgq_get_fd(q->outq) == fd);
|
||||
pa_assert(pa_asyncmsgq_read_fd(q->outq) == fd);
|
||||
pa_assert(events == PA_IO_EVENT_INPUT);
|
||||
|
||||
pa_asyncmsgq_ref(aq = q->outq);
|
||||
pa_asyncmsgq_after_poll(aq);
|
||||
pa_asyncmsgq_write_after_poll(aq);
|
||||
|
||||
for (;;) {
|
||||
pa_msgobject *object;
|
||||
|
|
@ -68,14 +68,24 @@ static void asyncmsgq_cb(pa_mainloop_api*api, pa_io_event* e, int fd, pa_io_even
|
|||
pa_asyncmsgq_done(aq, ret);
|
||||
}
|
||||
|
||||
if (pa_asyncmsgq_before_poll(aq) == 0)
|
||||
if (pa_asyncmsgq_read_before_poll(aq) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
pa_asyncmsgq_unref(aq);
|
||||
}
|
||||
|
||||
void pa_thread_mq_init(pa_thread_mq *q, pa_mainloop_api *mainloop) {
|
||||
static void asyncmsgq_write_cb(pa_mainloop_api*api, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata) {
|
||||
pa_thread_mq *q = userdata;
|
||||
|
||||
pa_assert(pa_asyncmsgq_write_fd(q->inq) == fd);
|
||||
pa_assert(events == PA_IO_EVENT_INPUT);
|
||||
|
||||
pa_asyncmsgq_write_after_poll(q->inq);
|
||||
pa_asyncmsgq_write_before_poll(q->inq);
|
||||
}
|
||||
|
||||
void pa_thread_mq_init(pa_thread_mq *q, pa_mainloop_api *mainloop, pa_rtpoll *rtpoll) {
|
||||
pa_assert(q);
|
||||
pa_assert(mainloop);
|
||||
|
||||
|
|
@ -83,15 +93,22 @@ void pa_thread_mq_init(pa_thread_mq *q, pa_mainloop_api *mainloop) {
|
|||
pa_assert_se(q->inq = pa_asyncmsgq_new(0));
|
||||
pa_assert_se(q->outq = pa_asyncmsgq_new(0));
|
||||
|
||||
pa_assert_se(pa_asyncmsgq_before_poll(q->outq) == 0);
|
||||
pa_assert_se(q->io_event = mainloop->io_new(mainloop, pa_asyncmsgq_get_fd(q->outq), PA_IO_EVENT_INPUT, asyncmsgq_cb, q));
|
||||
pa_assert_se(pa_asyncmsgq_read_before_poll(q->outq) == 0);
|
||||
pa_assert_se(q->read_event = mainloop->io_new(mainloop, pa_asyncmsgq_read_fd(q->outq), PA_IO_EVENT_INPUT, asyncmsgq_read_cb, q));
|
||||
|
||||
pa_asyncmsgq_write_before_poll(q->inq);
|
||||
pa_assert_se(q->write_event = mainloop->io_new(mainloop, pa_asyncmsgq_write_fd(q->inq), PA_IO_EVENT_INPUT, asyncmsgq_write_cb, q));
|
||||
|
||||
pa_rtpoll_item_new_asyncmsgq_read(rtpoll, PA_RTPOLL_EARLY, q->inq);
|
||||
pa_rtpoll_item_new_asyncmsgq_write(rtpoll, PA_RTPOLL_LATE, q->outq);
|
||||
}
|
||||
|
||||
void pa_thread_mq_done(pa_thread_mq *q) {
|
||||
pa_assert(q);
|
||||
|
||||
q->mainloop->io_free(q->io_event);
|
||||
q->io_event = NULL;
|
||||
q->mainloop->io_free(q->read_event);
|
||||
q->mainloop->io_free(q->write_event);
|
||||
q->read_event = q->write_event = NULL;
|
||||
|
||||
pa_asyncmsgq_unref(q->inq);
|
||||
pa_asyncmsgq_unref(q->outq);
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
#include <pulse/mainloop-api.h>
|
||||
#include <pulsecore/asyncmsgq.h>
|
||||
#include <pulsecore/rtpoll.h>
|
||||
|
||||
/* Two way communication between a thread and a mainloop. Before the
|
||||
* thread is started a pa_pthread_mq should be initialized and than
|
||||
|
|
@ -34,10 +35,10 @@
|
|||
typedef struct pa_thread_mq {
|
||||
pa_mainloop_api *mainloop;
|
||||
pa_asyncmsgq *inq, *outq;
|
||||
pa_io_event *io_event;
|
||||
pa_io_event *read_event, *write_event;
|
||||
} pa_thread_mq;
|
||||
|
||||
void pa_thread_mq_init(pa_thread_mq *q, pa_mainloop_api *mainloop);
|
||||
void pa_thread_mq_init(pa_thread_mq *q, pa_mainloop_api *mainloop, pa_rtpoll *rtpoll);
|
||||
void pa_thread_mq_done(pa_thread_mq *q);
|
||||
|
||||
/* Install the specified pa_thread_mq object for the current thread */
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@
|
|||
|
||||
#include "time-smoother.h"
|
||||
|
||||
#define HISTORY_MAX 50
|
||||
#define HISTORY_MAX 64
|
||||
|
||||
/*
|
||||
* Implementation of a time smoothing algorithm to synchronize remote
|
||||
|
|
@ -61,7 +61,6 @@
|
|||
|
||||
struct pa_smoother {
|
||||
pa_usec_t adjust_time, history_time;
|
||||
pa_bool_t monotonic;
|
||||
|
||||
pa_usec_t time_offset;
|
||||
|
||||
|
|
@ -70,27 +69,34 @@ struct pa_smoother {
|
|||
|
||||
pa_usec_t ex, ey; /* Point e, which we estimated before and need to smooth to */
|
||||
double de; /* Gradient we estimated for point e */
|
||||
pa_usec_t ry; /* The original y value for ex */
|
||||
|
||||
/* History of last measurements */
|
||||
pa_usec_t history_x[HISTORY_MAX], history_y[HISTORY_MAX];
|
||||
unsigned history_idx, n_history;
|
||||
|
||||
/* To even out for monotonicity */
|
||||
pa_usec_t last_y;
|
||||
pa_usec_t last_y, last_x;
|
||||
|
||||
/* Cached parameters for our interpolation polynomial y=ax^3+b^2+cx */
|
||||
double a, b, c;
|
||||
pa_bool_t abc_valid;
|
||||
|
||||
pa_bool_t paused;
|
||||
pa_bool_t monotonic:1;
|
||||
pa_bool_t paused:1;
|
||||
|
||||
pa_usec_t pause_time;
|
||||
|
||||
unsigned min_history;
|
||||
};
|
||||
|
||||
pa_smoother* pa_smoother_new(pa_usec_t adjust_time, pa_usec_t history_time, pa_bool_t monotonic) {
|
||||
pa_smoother* pa_smoother_new(pa_usec_t adjust_time, pa_usec_t history_time, pa_bool_t monotonic, unsigned min_history) {
|
||||
pa_smoother *s;
|
||||
|
||||
pa_assert(adjust_time > 0);
|
||||
pa_assert(history_time > 0);
|
||||
pa_assert(min_history >= 2);
|
||||
pa_assert(min_history <= HISTORY_MAX);
|
||||
|
||||
s = pa_xnew(pa_smoother, 1);
|
||||
s->adjust_time = adjust_time;
|
||||
|
|
@ -101,18 +107,20 @@ pa_smoother* pa_smoother_new(pa_usec_t adjust_time, pa_usec_t history_time, pa_b
|
|||
s->px = s->py = 0;
|
||||
s->dp = 1;
|
||||
|
||||
s->ex = s->ey = 0;
|
||||
s->ex = s->ey = s->ry = 0;
|
||||
s->de = 1;
|
||||
|
||||
s->history_idx = 0;
|
||||
s->n_history = 0;
|
||||
|
||||
s->last_y = 0;
|
||||
s->last_y = s->last_x = 0;
|
||||
|
||||
s->abc_valid = FALSE;
|
||||
|
||||
s->paused = FALSE;
|
||||
|
||||
s->min_history = min_history;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
|
@ -122,39 +130,58 @@ void pa_smoother_free(pa_smoother* s) {
|
|||
pa_xfree(s);
|
||||
}
|
||||
|
||||
#define REDUCE(x) \
|
||||
do { \
|
||||
x = (x) % HISTORY_MAX; \
|
||||
} while(FALSE)
|
||||
|
||||
#define REDUCE_INC(x) \
|
||||
do { \
|
||||
x = ((x)+1) % HISTORY_MAX; \
|
||||
} while(FALSE)
|
||||
|
||||
|
||||
static void drop_old(pa_smoother *s, pa_usec_t x) {
|
||||
unsigned j;
|
||||
|
||||
/* First drop items from history which are too old, but make sure
|
||||
* to always keep two entries in the history */
|
||||
/* Drop items from history which are too old, but make sure to
|
||||
* always keep min_history in the history */
|
||||
|
||||
for (j = s->n_history; j > 2; j--) {
|
||||
while (s->n_history > s->min_history) {
|
||||
|
||||
if (s->history_x[s->history_idx] + s->history_time >= x) {
|
||||
if (s->history_x[s->history_idx] + s->history_time >= x)
|
||||
/* This item is still valid, and thus all following ones
|
||||
* are too, so let's quit this loop */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Item is too old, let's drop it */
|
||||
s->history_idx ++;
|
||||
while (s->history_idx >= HISTORY_MAX)
|
||||
s->history_idx -= HISTORY_MAX;
|
||||
REDUCE_INC(s->history_idx);
|
||||
|
||||
s->n_history --;
|
||||
}
|
||||
}
|
||||
|
||||
static void add_to_history(pa_smoother *s, pa_usec_t x, pa_usec_t y) {
|
||||
unsigned j;
|
||||
unsigned j, i;
|
||||
pa_assert(s);
|
||||
|
||||
/* First try to update an existing history entry */
|
||||
i = s->history_idx;
|
||||
for (j = s->n_history; j > 0; j--) {
|
||||
|
||||
if (s->history_x[i] == x) {
|
||||
s->history_y[i] = y;
|
||||
return;
|
||||
}
|
||||
|
||||
REDUCE_INC(i);
|
||||
}
|
||||
|
||||
/* Drop old entries */
|
||||
drop_old(s, x);
|
||||
|
||||
/* Calculate position for new entry */
|
||||
j = s->history_idx + s->n_history;
|
||||
while (j >= HISTORY_MAX)
|
||||
j -= HISTORY_MAX;
|
||||
REDUCE(j);
|
||||
|
||||
/* Fill in entry */
|
||||
s->history_x[j] = x;
|
||||
|
|
@ -164,8 +191,9 @@ static void add_to_history(pa_smoother *s, pa_usec_t x, pa_usec_t y) {
|
|||
s->n_history ++;
|
||||
|
||||
/* And make sure we don't store more entries than fit in */
|
||||
if (s->n_history >= HISTORY_MAX) {
|
||||
if (s->n_history > HISTORY_MAX) {
|
||||
s->history_idx += s->n_history - HISTORY_MAX;
|
||||
REDUCE(s->history_idx);
|
||||
s->n_history = HISTORY_MAX;
|
||||
}
|
||||
}
|
||||
|
|
@ -175,7 +203,9 @@ static double avg_gradient(pa_smoother *s, pa_usec_t x) {
|
|||
int64_t ax = 0, ay = 0, k, t;
|
||||
double r;
|
||||
|
||||
drop_old(s, x);
|
||||
/* Too few measurements, assume gradient of 1 */
|
||||
if (s->n_history < s->min_history)
|
||||
return 1;
|
||||
|
||||
/* First, calculate average of all measurements */
|
||||
i = s->history_idx;
|
||||
|
|
@ -185,15 +215,10 @@ static double avg_gradient(pa_smoother *s, pa_usec_t x) {
|
|||
ay += s->history_y[i];
|
||||
c++;
|
||||
|
||||
i++;
|
||||
while (i >= HISTORY_MAX)
|
||||
i -= HISTORY_MAX;
|
||||
REDUCE_INC(i);
|
||||
}
|
||||
|
||||
/* Too few measurements, assume gradient of 1 */
|
||||
if (c < 2)
|
||||
return 1;
|
||||
|
||||
pa_assert(c >= s->min_history);
|
||||
ax /= c;
|
||||
ay /= c;
|
||||
|
||||
|
|
@ -210,14 +235,45 @@ static double avg_gradient(pa_smoother *s, pa_usec_t x) {
|
|||
k += dx*dy;
|
||||
t += dx*dx;
|
||||
|
||||
i++;
|
||||
while (i >= HISTORY_MAX)
|
||||
i -= HISTORY_MAX;
|
||||
REDUCE_INC(i);
|
||||
}
|
||||
|
||||
r = (double) k / t;
|
||||
|
||||
return s->monotonic && r < 0 ? 0 : r;
|
||||
return (s->monotonic && r < 0) ? 0 : r;
|
||||
}
|
||||
|
||||
static void calc_abc(pa_smoother *s) {
|
||||
pa_usec_t ex, ey, px, py;
|
||||
int64_t kx, ky;
|
||||
double de, dp;
|
||||
|
||||
pa_assert(s);
|
||||
|
||||
if (s->abc_valid)
|
||||
return;
|
||||
|
||||
/* We have two points: (ex|ey) and (px|py) with two gradients at
|
||||
* these points de and dp. We do a polynomial
|
||||
* interpolation of degree 3 with these 6 values */
|
||||
|
||||
ex = s->ex; ey = s->ey;
|
||||
px = s->px; py = s->py;
|
||||
de = s->de; dp = s->dp;
|
||||
|
||||
pa_assert(ex < px);
|
||||
|
||||
/* To increase the dynamic range and symplify calculation, we
|
||||
* move these values to the origin */
|
||||
kx = (int64_t) px - (int64_t) ex;
|
||||
ky = (int64_t) py - (int64_t) ey;
|
||||
|
||||
/* Calculate a, b, c for y=ax^3+bx^2+cx */
|
||||
s->c = de;
|
||||
s->b = (((double) (3*ky)/kx - dp - 2*de)) / kx;
|
||||
s->a = (dp/kx - 2*s->b - de/kx) / (3*kx);
|
||||
|
||||
s->abc_valid = TRUE;
|
||||
}
|
||||
|
||||
static void estimate(pa_smoother *s, pa_usec_t x, pa_usec_t *y, double *deriv) {
|
||||
|
|
@ -242,36 +298,10 @@ static void estimate(pa_smoother *s, pa_usec_t x, pa_usec_t *y, double *deriv) {
|
|||
|
||||
} else {
|
||||
|
||||
if (!s->abc_valid) {
|
||||
pa_usec_t ex, ey, px, py;
|
||||
int64_t kx, ky;
|
||||
double de, dp;
|
||||
/* Ok, we're not yet on track, thus let's interpolate, and
|
||||
* make sure that the first derivative is smooth */
|
||||
|
||||
/* Ok, we're not yet on track, thus let's interpolate, and
|
||||
* make sure that the first derivative is smooth */
|
||||
|
||||
/* We have two points: (ex|ey) and (px|py) with two gradients
|
||||
* at these points de and dp. We do a polynomial interpolation
|
||||
* of degree 3 with these 6 values */
|
||||
|
||||
ex = s->ex; ey = s->ey;
|
||||
px = s->px; py = s->py;
|
||||
de = s->de; dp = s->dp;
|
||||
|
||||
pa_assert(ex < px);
|
||||
|
||||
/* To increase the dynamic range and symplify calculation, we
|
||||
* move these values to the origin */
|
||||
kx = (int64_t) px - (int64_t) ex;
|
||||
ky = (int64_t) py - (int64_t) ey;
|
||||
|
||||
/* Calculate a, b, c for y=ax^3+b^2+cx */
|
||||
s->c = de;
|
||||
s->b = (((double) (3*ky)/kx - dp - 2*de)) / kx;
|
||||
s->a = (dp/kx - 2*s->b - de/kx) / (3*kx);
|
||||
|
||||
s->abc_valid = TRUE;
|
||||
}
|
||||
calc_abc(s);
|
||||
|
||||
/* Move to origin */
|
||||
x -= s->ex;
|
||||
|
|
@ -290,11 +320,6 @@ static void estimate(pa_smoother *s, pa_usec_t x, pa_usec_t *y, double *deriv) {
|
|||
/* Guarantee monotonicity */
|
||||
if (s->monotonic) {
|
||||
|
||||
if (*y < s->last_y)
|
||||
*y = s->last_y;
|
||||
else
|
||||
s->last_y = *y;
|
||||
|
||||
if (deriv && *deriv < 0)
|
||||
*deriv = 0;
|
||||
}
|
||||
|
|
@ -303,23 +328,26 @@ static void estimate(pa_smoother *s, pa_usec_t x, pa_usec_t *y, double *deriv) {
|
|||
void pa_smoother_put(pa_smoother *s, pa_usec_t x, pa_usec_t y) {
|
||||
pa_usec_t ney;
|
||||
double nde;
|
||||
pa_bool_t is_new;
|
||||
|
||||
pa_assert(s);
|
||||
pa_assert(x >= s->time_offset);
|
||||
|
||||
/* Fix up x value */
|
||||
if (s->paused)
|
||||
x = s->pause_time;
|
||||
|
||||
pa_assert(x >= s->time_offset);
|
||||
x -= s->time_offset;
|
||||
x = PA_LIKELY(x >= s->time_offset) ? x - s->time_offset : 0;
|
||||
|
||||
pa_assert(x >= s->ex);
|
||||
is_new = x >= s->ex;
|
||||
|
||||
/* First, we calculate the position we'd estimate for x, so that
|
||||
* we can adjust our position smoothly from this one */
|
||||
estimate(s, x, &ney, &nde);
|
||||
s->ex = x; s->ey = ney; s->de = nde;
|
||||
if (is_new) {
|
||||
/* First, we calculate the position we'd estimate for x, so that
|
||||
* we can adjust our position smoothly from this one */
|
||||
estimate(s, x, &ney, &nde);
|
||||
s->ex = x; s->ey = ney; s->de = nde;
|
||||
|
||||
s->ry = y;
|
||||
}
|
||||
|
||||
/* Then, we add the new measurement to our history */
|
||||
add_to_history(s, x, y);
|
||||
|
|
@ -328,28 +356,41 @@ void pa_smoother_put(pa_smoother *s, pa_usec_t x, pa_usec_t y) {
|
|||
s->dp = avg_gradient(s, x);
|
||||
|
||||
/* And calculate when we want to be on track again */
|
||||
s->px = x + s->adjust_time;
|
||||
s->py = y + s->dp *s->adjust_time;
|
||||
s->px = s->ex + s->adjust_time;
|
||||
s->py = s->ry + s->dp *s->adjust_time;
|
||||
|
||||
s->abc_valid = FALSE;
|
||||
|
||||
/* pa_log_debug("put(%llu | %llu) = %llu", (unsigned long long) (x + s->time_offset), (unsigned long long) x, (unsigned long long) y); */
|
||||
}
|
||||
|
||||
pa_usec_t pa_smoother_get(pa_smoother *s, pa_usec_t x) {
|
||||
pa_usec_t y;
|
||||
|
||||
pa_assert(s);
|
||||
pa_assert(x >= s->time_offset);
|
||||
|
||||
/* Fix up x value */
|
||||
if (s->paused)
|
||||
x = s->pause_time;
|
||||
|
||||
pa_assert(x >= s->time_offset);
|
||||
x -= s->time_offset;
|
||||
|
||||
pa_assert(x >= s->ex);
|
||||
x = PA_LIKELY(x >= s->time_offset) ? x - s->time_offset : 0;
|
||||
|
||||
estimate(s, x, &y, NULL);
|
||||
|
||||
if (s->monotonic) {
|
||||
|
||||
/* Make sure the querier doesn't jump forth and back. */
|
||||
pa_assert(x >= s->last_x);
|
||||
s->last_x = x;
|
||||
|
||||
if (y < s->last_y)
|
||||
y = s->last_y;
|
||||
else
|
||||
s->last_y = y;
|
||||
}
|
||||
|
||||
/* pa_log_debug("get(%llu | %llu) = %llu", (unsigned long long) (x + s->time_offset), (unsigned long long) x, (unsigned long long) y); */
|
||||
|
||||
return y;
|
||||
}
|
||||
|
||||
|
|
@ -357,6 +398,8 @@ void pa_smoother_set_time_offset(pa_smoother *s, pa_usec_t offset) {
|
|||
pa_assert(s);
|
||||
|
||||
s->time_offset = offset;
|
||||
|
||||
/* pa_log_debug("offset(%llu)", (unsigned long long) offset); */
|
||||
}
|
||||
|
||||
void pa_smoother_pause(pa_smoother *s, pa_usec_t x) {
|
||||
|
|
@ -365,6 +408,8 @@ void pa_smoother_pause(pa_smoother *s, pa_usec_t x) {
|
|||
if (s->paused)
|
||||
return;
|
||||
|
||||
/* pa_log_debug("pause(%llu)", (unsigned long long) x); */
|
||||
|
||||
s->paused = TRUE;
|
||||
s->pause_time = x;
|
||||
}
|
||||
|
|
@ -377,6 +422,32 @@ void pa_smoother_resume(pa_smoother *s, pa_usec_t x) {
|
|||
|
||||
pa_assert(x >= s->pause_time);
|
||||
|
||||
/* pa_log_debug("resume(%llu)", (unsigned long long) x); */
|
||||
|
||||
s->paused = FALSE;
|
||||
s->time_offset += x - s->pause_time;
|
||||
}
|
||||
|
||||
pa_usec_t pa_smoother_translate(pa_smoother *s, pa_usec_t x, pa_usec_t y_delay) {
|
||||
pa_usec_t ney;
|
||||
double nde;
|
||||
|
||||
pa_assert(s);
|
||||
|
||||
/* Fix up x value */
|
||||
if (s->paused)
|
||||
x = s->pause_time;
|
||||
|
||||
x = PA_LIKELY(x >= s->time_offset) ? x - s->time_offset : 0;
|
||||
|
||||
estimate(s, x, &ney, &nde);
|
||||
|
||||
/* Play safe and take the larger gradient, so that we wakeup
|
||||
* earlier when this is used for sleeping */
|
||||
if (s->dp > nde)
|
||||
nde = s->dp;
|
||||
|
||||
/* pa_log_debug("translate(%llu) = %llu (%0.2f)", (unsigned long long) y_delay, (unsigned long long) ((double) y_delay / nde), nde); */
|
||||
|
||||
return (pa_usec_t) ((double) y_delay / nde);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,13 +29,19 @@
|
|||
|
||||
typedef struct pa_smoother pa_smoother;
|
||||
|
||||
pa_smoother* pa_smoother_new(pa_usec_t adjust_time, pa_usec_t history_time, pa_bool_t monotonic);
|
||||
pa_smoother* pa_smoother_new(pa_usec_t x_adjust_time, pa_usec_t x_history_time, pa_bool_t monotonic, unsigned min_history);
|
||||
void pa_smoother_free(pa_smoother* s);
|
||||
|
||||
/* Adds a new value to our dataset. x = local/system time, y = remote time */
|
||||
void pa_smoother_put(pa_smoother *s, pa_usec_t x, pa_usec_t y);
|
||||
|
||||
/* Returns an interpolated value based on the dataset. x = local/system time, return value = remote time */
|
||||
pa_usec_t pa_smoother_get(pa_smoother *s, pa_usec_t x);
|
||||
|
||||
void pa_smoother_set_time_offset(pa_smoother *s, pa_usec_t offset);
|
||||
/* Translates a time span from the remote time domain to the local one. x = local/system time when to estimate, y_delay = remote time span */
|
||||
pa_usec_t pa_smoother_translate(pa_smoother *s, pa_usec_t x, pa_usec_t y_delay);
|
||||
|
||||
void pa_smoother_set_time_offset(pa_smoother *s, pa_usec_t x_offset);
|
||||
|
||||
void pa_smoother_pause(pa_smoother *s, pa_usec_t x);
|
||||
void pa_smoother_resume(pa_smoother *s, pa_usec_t x);
|
||||
|
|
|
|||
|
|
@ -29,9 +29,9 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include <pulse/xmalloc.h>
|
||||
#include <pulse/gccmacro.h>
|
||||
|
||||
#include <pulsecore/dynarray.h>
|
||||
#include <pulsecore/gccmacro.h>
|
||||
#include <pulsecore/macro.h>
|
||||
|
||||
#include "tokenizer.h"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue