diff --git a/src/context.c b/src/context.c index 5dda95852..1df2625de 100644 --- a/src/context.c +++ b/src/context.c @@ -260,7 +260,6 @@ static void context_free(pa_context *c) if (c->proplist) pa_proplist_free(c->proplist); - free(c); } void pa_context_unref(pa_context *c) diff --git a/src/ext-stream-restore.c b/src/ext-stream-restore.c new file mode 100644 index 000000000..039c6bfb6 --- /dev/null +++ b/src/ext-stream-restore.c @@ -0,0 +1,90 @@ +/* PipeWire + * Copyright (C) 2018 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include + +#include + +#include "internal.h" + +/** Test if this extension module is available in the server. \since 0.9.12 */ +pa_operation *pa_ext_stream_restore_test( + pa_context *c, + pa_ext_stream_restore_test_cb_t cb, + void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +/** Read all entries from the stream database. \since 0.9.12 */ +pa_operation *pa_ext_stream_restore_read( + pa_context *c, + pa_ext_stream_restore_read_cb_t cb, + void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +/** Store entries in the stream database. \since 0.9.12 */ +pa_operation *pa_ext_stream_restore_write( + pa_context *c, + pa_update_mode_t mode, + const pa_ext_stream_restore_info data[], + unsigned n, + int apply_immediately, + pa_context_success_cb_t cb, + void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +/** Delete entries from the stream database. \since 0.9.12 */ +pa_operation *pa_ext_stream_restore_delete( + pa_context *c, + const char *const s[], + pa_context_success_cb_t cb, + void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +/** Subscribe to changes in the stream database. \since 0.9.12 */ +pa_operation *pa_ext_stream_restore_subscribe( + pa_context *c, + int enable, + pa_context_success_cb_t cb, + void *userdata) +{ + pw_log_warn("Not Implemented"); + return NULL; +} + +/** Set the subscription callback that is called when + * pa_ext_stream_restore_subscribe() was called. \since 0.9.12 */ +void pa_ext_stream_restore_set_subscribe_cb( + pa_context *c, + pa_ext_stream_restore_subscribe_cb_t cb, + void *userdata) +{ + pw_log_warn("Not Implemented"); +} diff --git a/src/internal.h b/src/internal.h index da42139d2..05671af75 100644 --- a/src/internal.h +++ b/src/internal.h @@ -24,11 +24,13 @@ #include #include +#include #include #include #include #include +#include #include #include @@ -44,13 +46,28 @@ extern "C" { #define pa_strneq(a,b,n) (!strncmp((a),(b),(n))) #define PA_UNLIKELY SPA_UNLIKELY +#define PA_LIKELY SPA_LIKELY #define PA_MIN SPA_MIN #define PA_MAX SPA_MAX #define pa_assert spa_assert +#define pa_assert_se spa_assert #define pa_return_val_if_fail spa_return_val_if_fail #define pa_assert_not_reached spa_assert_not_reached -#define PA_USEC_PER_MSEC SPA_USEC_PER_MSEC +#define PA_INT_TYPE_SIGNED(type) (!!((type) 0 > (type) -1)) + +#define PA_INT_TYPE_HALF(type) ((type) 1 << (sizeof(type)*8 - 2)) + +#define PA_INT_TYPE_MAX(type) \ + ((type) (PA_INT_TYPE_SIGNED(type) \ + ? (PA_INT_TYPE_HALF(type) - 1 + PA_INT_TYPE_HALF(type)) \ + : (type) -1)) + +#define PA_INT_TYPE_MIN(type) \ + ((type) (PA_INT_TYPE_SIGNED(type) \ + ? (-1 - PA_INT_TYPE_MAX(type)) \ + : (type) 0)) + #ifdef __GNUC__ #define PA_CLAMP_UNLIKELY(x, low, high) \ @@ -183,6 +200,9 @@ struct pa_context { void *state_userdata; pa_context_event_cb_t event_callback; void *event_userdata; + pa_context_subscribe_cb_t subscribe_callback; + void *subscribe_userdata; + pa_subscription_mask_t subscribe_mask; bool no_fail; uint32_t client_index; @@ -210,6 +230,9 @@ static inline void init_type(struct type *type, struct spa_type_map *map) spa_type_audio_format_map(map, &type->audio_format); } +#define MAX_BUFFERS 64 +#define MASK_BUFFERS (MAX_BUFFERS-1) + struct pa_stream { struct spa_list link; int refcount; @@ -270,7 +293,17 @@ struct pa_stream { pa_stream_notify_cb_t buffer_attr_callback; void *buffer_attr_userdata; + int64_t offset; + + struct pw_buffer *dequeued[MAX_BUFFERS]; + struct spa_ringbuffer dequeued_ring; + size_t dequeued_size; + struct pw_buffer *buffer; + void *buffer_data; + uint32_t buffer_index; + int64_t buffer_size; + int64_t buffer_offset; }; void pa_stream_set_state(pa_stream *s, pa_stream_state_t st); diff --git a/src/introspect.c b/src/introspect.c index db8982c89..ce2af79a8 100644 --- a/src/introspect.c +++ b/src/introspect.c @@ -25,12 +25,12 @@ #include "internal.h" -typedef int (*global_filter_t)(pa_context *c, struct global *g); +typedef int (*global_filter_t)(pa_context *c, struct global *g, bool full); static void node_event_info(void *object, struct pw_node_info *info) { struct global *g = object; - pw_log_debug("update"); + pw_log_debug("update %d", g->id); g->info = pw_node_info_update(g->info, info); } @@ -42,7 +42,7 @@ static const struct pw_node_proxy_events node_events = { static void module_event_info(void *object, struct pw_module_info *info) { struct global *g = object; - pw_log_debug("update"); + pw_log_debug("update %d", g->id); g->info = pw_module_info_update(g->info, info); } @@ -54,7 +54,7 @@ static const struct pw_module_proxy_events module_events = { static void client_event_info(void *object, struct pw_client_info *info) { struct global *g = object; - pw_log_debug("update"); + pw_log_debug("update %d", g->id); g->info = pw_client_info_update(g->info, info); } @@ -91,6 +91,8 @@ static int ensure_global(pa_context *c, struct global *g) else return -EINVAL; + pw_log_debug("bind %d", g->id); + g->proxy = pw_registry_proxy_bind(c->registry_proxy, g->id, g->type, client_version, 0); if (g->proxy == NULL) @@ -106,7 +108,7 @@ static void ensure_types(pa_context *c, uint32_t type, global_filter_t filter) { struct global *g; spa_list_for_each(g, &c->globals, link) { - if (!filter(c, g)) + if (!filter(c, g, false)) continue; ensure_global(c, g); } @@ -124,6 +126,8 @@ static void sink_callback(struct sink_data *d) struct global *g = d->global; struct pw_node_info *info = g->info; pa_sink_info i; + pa_format_info ii[1]; + pa_format_info *ip[1]; spa_zero(i); i.index = g->id; @@ -132,7 +136,11 @@ static void sink_callback(struct sink_data *d) i.owner_module = g->parent_id; i.base_volume = PA_VOLUME_NORM; i.n_volume_steps = PA_VOLUME_NORM+1; - + i.n_formats = 1; + ii[0].encoding = PA_ENCODING_PCM; + ii[0].plist = pa_proplist_new(); + ip[0] = ii; + i.formats = ip; d->cb(d->context, &i, 0, d->userdata); } @@ -141,9 +149,10 @@ static void sink_info(pa_operation *o, void *userdata) struct sink_data *d = userdata; sink_callback(d); d->cb(d->context, NULL, 1, d->userdata); + pa_operation_done(o); } -static int sink_filter(pa_context *c, struct global *g) +static int sink_filter(pa_context *c, struct global *g, bool full) { const char *str; @@ -158,10 +167,41 @@ static int sink_filter(pa_context *c, struct global *g) return 1; } +static struct global *find_sink_by_name(pa_context *c, const char *name) +{ + struct global *g; + spa_list_for_each(g, &c->globals, link) { + if (sink_filter(c, g, true)) + return g; + } + return NULL; +} + pa_operation* pa_context_get_sink_info_by_name(pa_context *c, const char *name, pa_sink_info_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); - return NULL; + pa_operation *o; + struct global *g; + struct sink_data *d; + + pa_assert(c); + pa_assert(c->refcount >= 1); + pa_assert(cb); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID); + + if ((g = find_sink_by_name(c, name)) == NULL) + return NULL; + + ensure_global(c, g); + + o = pa_operation_new(c, NULL, sink_info, sizeof(struct sink_data)); + d = o->userdata; + d->context = c; + d->cb = cb; + d->userdata = userdata; + d->global = g; + return o; } pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t idx, pa_sink_info_cb_t cb, void *userdata) @@ -174,17 +214,21 @@ pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t idx, pa_ pa_assert(c->refcount >= 1); pa_assert(cb); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); if ((g = pa_context_find_global(c, idx)) == NULL) return NULL; - if (!sink_filter(c, g)) + if (!sink_filter(c, g, false)) return NULL; ensure_global(c, g); o = pa_operation_new(c, NULL, sink_info, sizeof(struct sink_data)); d = o->userdata; + d->context = c; + d->cb = cb; + d->userdata = userdata; d->global = g; return o; } @@ -196,12 +240,13 @@ static void sink_info_list(pa_operation *o, void *userdata) struct global *g; spa_list_for_each(g, &c->globals, link) { - if (!sink_filter(c, g)) + if (!sink_filter(c, g, true)) continue; d->global = g; sink_callback(d); } d->cb(c, NULL, 1, d->userdata); + pa_operation_done(o); } pa_operation* pa_context_get_sink_info_list(pa_context *c, pa_sink_info_cb_t cb, void *userdata) @@ -305,7 +350,7 @@ static void source_info(pa_operation *o, void *userdata) d->cb(d->context, NULL, 1, d->userdata); } -static int source_filter(pa_context *c, struct global *g) +static int source_filter(pa_context *c, struct global *g, bool full) { const char *str; @@ -340,7 +385,7 @@ pa_operation* pa_context_get_source_info_by_index(pa_context *c, uint32_t idx, p if ((g = pa_context_find_global(c, idx)) == NULL) return NULL; - if (!source_filter(c, g)) + if (!source_filter(c, g, false)) return NULL; ensure_global(c, g); @@ -358,7 +403,7 @@ static void source_info_list(pa_operation *o, void *userdata) struct global *g; spa_list_for_each(g, &c->globals, link) { - if (!source_filter(c, g)) + if (!source_filter(c, g, true)) continue; d->global = g; source_callback(d); @@ -471,7 +516,7 @@ static void module_info(pa_operation *o, void *userdata) d->cb(d->context, NULL, 1, d->userdata); } -static int module_filter(pa_context *c, struct global *g) +static int module_filter(pa_context *c, struct global *g, bool full) { if (g->type != c->t->module) return 0; @@ -492,7 +537,7 @@ pa_operation* pa_context_get_module_info(pa_context *c, uint32_t idx, pa_module_ if ((g = pa_context_find_global(c, idx)) == NULL) return NULL; - if (!module_filter(c, g)) + if (!module_filter(c, g, false)) return NULL; ensure_global(c, g); @@ -511,7 +556,7 @@ static void module_info_list(pa_operation *o, void *userdata) struct global *g; spa_list_for_each(g, &c->globals, link) { - if (!module_filter(c, g)) + if (!module_filter(c, g, true)) continue; d->global = g; module_callback(d); @@ -583,7 +628,7 @@ static void client_info(pa_operation *o, void *userdata) d->cb(d->context, NULL, 1, d->userdata); } -static int client_filter(pa_context *c, struct global *g) +static int client_filter(pa_context *c, struct global *g, bool full) { if (g->type != c->t->client) return 0; @@ -604,7 +649,7 @@ pa_operation* pa_context_get_client_info(pa_context *c, uint32_t idx, pa_client_ if ((g = pa_context_find_global(c, idx)) == NULL) return NULL; - if (!client_filter(c, g)) + if (!client_filter(c, g, false)) return NULL; ensure_global(c, g); @@ -623,7 +668,7 @@ static void client_info_list(pa_operation *o, void *userdata) struct global *g; spa_list_for_each(g, &c->globals, link) { - if (!client_filter(c, g)) + if (!client_filter(c, g, true)) continue; d->global = g; client_callback(d); @@ -694,16 +739,118 @@ pa_operation* pa_context_set_port_latency_offset(pa_context *c, const char *card return NULL; } +struct sink_input_data { + pa_context *context; + pa_sink_input_info_cb_t cb; + void *userdata; + struct global *global; +}; + +static void sink_input_callback(struct sink_input_data *d) +{ + struct global *g = d->global; + struct pw_node_info *info = g->info; + pa_sink_input_info i; + pa_format_info ii[1]; + + spa_zero(i); + i.index = g->id; + i.name = info->name; + i.proplist = pa_proplist_new_dict(info->props); + i.owner_module = g->parent_id; + ii[0].encoding = PA_ENCODING_PCM; + ii[0].plist = pa_proplist_new(); + i.format = ii; + + d->cb(d->context, &i, 0, d->userdata); +} + +static void sink_input_info(pa_operation *o, void *userdata) +{ + struct sink_input_data *d = userdata; + sink_input_callback(d); + d->cb(d->context, NULL, 1, d->userdata); +} + +static int sink_input_filter(pa_context *c, struct global *g, bool full) +{ + const char *str; + struct pw_node_info *info = g->info; + + if (g->type != c->t->node) + return 0; + + if (full) { + if (info == NULL || info->props == NULL) + return 0; + if ((str = spa_dict_lookup(info->props, "node.stream")) == NULL) + return 0; + if (pw_properties_parse_bool(str) == false) + return 0; + if (info->n_output_ports == 0) + return 0; + } + return 1; +} + pa_operation* pa_context_get_sink_input_info(pa_context *c, uint32_t idx, pa_sink_input_info_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); - return NULL; + pa_operation *o; + struct global *g; + struct sink_input_data *d; + + pa_assert(c); + pa_assert(c->refcount >= 1); + pa_assert(cb); + + PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); + + if ((g = pa_context_find_global(c, idx)) == NULL) + return NULL; + if (!sink_input_filter(c, g, false)) + return NULL; + + ensure_global(c, g); + + o = pa_operation_new(c, NULL, sink_input_info, sizeof(struct sink_input_data)); + d = o->userdata; + d->global = g; + return o; +} + +static void sink_input_info_list(pa_operation *o, void *userdata) +{ + struct sink_input_data *d = userdata; + pa_context *c = d->context; + struct global *g; + + spa_list_for_each(g, &c->globals, link) { + if (!sink_input_filter(c, g, true)) + continue; + d->global = g; + sink_input_callback(d); + } + d->cb(c, NULL, 1, d->userdata); } pa_operation* pa_context_get_sink_input_info_list(pa_context *c, pa_sink_input_info_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); - return NULL; + pa_operation *o; + struct sink_input_data *d; + + pa_assert(c); + pa_assert(c->refcount >= 1); + pa_assert(cb); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + + ensure_types(c, c->t->node, sink_input_filter); + o = pa_operation_new(c, NULL, sink_input_info_list, sizeof(struct sink_input_data)); + d = o->userdata; + d->context = c; + d->cb = cb; + d->userdata = userdata; + return o; } pa_operation* pa_context_move_sink_input_by_name(pa_context *c, uint32_t idx, const char *sink_name, pa_context_success_cb_t cb, void* userdata) diff --git a/src/meson.build b/src/meson.build index 1a7e58c63..862c8670f 100644 --- a/src/meson.build +++ b/src/meson.build @@ -7,6 +7,7 @@ pipewire_pulseaudio_sources = [ 'error.c', 'ext-device-manager.c', 'ext-device-restore.c', + 'ext-stream-restore.c', 'format.c', 'introspect.c', 'json.c', @@ -20,6 +21,8 @@ pipewire_pulseaudio_sources = [ 'stream.c', 'strbuf.c', 'subscribe.c', + 'thread-mainloop.c', + 'timeval.c', 'utf8.c', 'util.c', 'version.c', diff --git a/src/proplist.c b/src/proplist.c index 999b86d6e..4ed222c3b 100644 --- a/src/proplist.c +++ b/src/proplist.c @@ -85,8 +85,13 @@ int pa_proplist_setp(pa_proplist *p, const char *pair) int pa_proplist_setf(pa_proplist *p, const char *key, const char *format, ...) { - pw_log_warn("Not Implemented"); - return -1; + va_list varargs; + + va_start(varargs, format); + pw_properties_setva(p->props, key, format, varargs); + va_end(varargs); + + return 0; } int pa_proplist_set(pa_proplist *p, const char *key, const void *data, size_t nbytes) diff --git a/src/stream.c b/src/stream.c index 0256be281..9820e2528 100644 --- a/src/stream.c +++ b/src/stream.c @@ -23,6 +23,7 @@ #include #include +#include #include #include "internal.h" @@ -56,16 +57,36 @@ static void stream_state_changed(void *data, enum pw_stream_state old, static void stream_format_changed(void *data, const struct spa_pod *format) { -} + pa_stream *s = data; + s->sample_spec.format = PA_SAMPLE_S16NE, + s->sample_spec.rate = 44100; + s->sample_spec.channels = 2; + + if (s->format) + pa_format_info_free(s->format); + s->format = pa_format_info_from_sample_spec(&s->sample_spec, NULL); +} static void stream_process(void *data) { pa_stream *s = data; if (s->direction == PA_STREAM_PLAYBACK) { + struct pw_buffer *buf; + uint32_t index; + + buf = pw_stream_dequeue_buffer(s->stream); + if (buf != NULL) { + spa_ringbuffer_get_write_index(&s->dequeued_ring, &index); + s->dequeued[index & MASK_BUFFERS] = buf; + spa_ringbuffer_write_update(&s->dequeued_ring, index + 1); + + s->dequeued_size += buf->buffer->datas[0].maxsize; + } + if (s->write_callback) - s->write_callback(s, 4096, s->write_userdata); + s->write_callback(s, s->dequeued_size, s->write_userdata); } else { if (s->read_callback) @@ -159,6 +180,8 @@ pa_stream* stream_new(pa_context *c, const char *name, s->timing_info_valid = false; + spa_ringbuffer_init(&s->dequeued_ring); + spa_list_append(&c->streams, &s->link); pa_stream_ref(s); @@ -358,6 +381,12 @@ static int create_stream(pa_stream_direction_t direction, ":", s->type.format_audio.channels, "i", 2, ":", s->type.format_audio.rate, "i", 44100); + if (attr) + s->buffer_attr = *attr; + + if (dev == NULL) + dev = getenv("PIPEWIRE_NODE"); + res = pw_stream_connect(s->stream, direction == PA_STREAM_PLAYBACK ? PW_DIRECTION_OUTPUT : @@ -389,14 +418,23 @@ int pa_stream_connect_record( return create_stream(PA_STREAM_RECORD, s, dev, attr, flags, NULL, NULL); } +static void on_disconnected(pa_operation *o, void *userdata) +{ + pa_stream_set_state(o->stream, PA_STREAM_TERMINATED); +} + int pa_stream_disconnect(pa_stream *s) { + pa_operation *o; + spa_assert(s); spa_assert(s->refcount >= 1); PA_CHECK_VALIDITY(s->context, s->context->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); - pa_stream_set_state(s, PA_STREAM_TERMINATED); + pw_stream_disconnect(s->stream); + o = pa_operation_new(s->context, s, on_disconnected, 0); + pa_operation_unref(o); return 0; } @@ -405,6 +443,9 @@ int pa_stream_begin_write( void **data, size_t *nbytes) { + int32_t avail; + uint32_t index; + spa_assert(s); spa_assert(s->refcount >= 1); @@ -415,16 +456,21 @@ int pa_stream_begin_write( PA_CHECK_VALIDITY(s->context, nbytes && *nbytes != 0, PA_ERR_INVALID); if (s->buffer == NULL) { - s->buffer = pw_stream_dequeue_buffer(s->stream); - } - if (s->buffer == NULL) { - *data = NULL; - *nbytes = 0; - } - else { - *data = s->buffer->buffer->datas[0].data; - *nbytes = s->buffer->buffer->datas[0].maxsize; + if ((avail = spa_ringbuffer_get_read_index(&s->dequeued_ring, &index)) <= 0) { + *data = NULL; + *nbytes = 0; + return 0; + } + s->buffer = s->dequeued[index & MASK_BUFFERS]; + s->buffer_index = index; + s->buffer_data = s->buffer->buffer->datas[0].data; + s->buffer_size = s->buffer->buffer->datas[0].maxsize; + s->buffer_offset = 0; } + + *data = SPA_MEMBER(s->buffer_data, s->buffer_offset, void); + *nbytes = s->buffer_size - s->buffer_offset; + return 0; } @@ -436,7 +482,9 @@ int pa_stream_cancel_write(pa_stream *s) PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_PLAYBACK || s->direction == PA_STREAM_UPLOAD, PA_ERR_BADSTATE); - pw_log_warn("Not Implemented"); + + s->buffer = NULL; + return 0; } @@ -475,11 +523,16 @@ int pa_stream_write_ext_free(pa_stream *s, pw_log_warn("Not Implemented"); return PA_ERR_INVALID; } - else { - s->buffer->buffer->datas[0].chunk->offset = 0; - s->buffer->buffer->datas[0].chunk->size = nbytes; - } + + s->buffer->buffer->datas[0].chunk->offset = 0; + s->buffer->buffer->datas[0].chunk->size = nbytes; + + s->dequeued_size -= s->buffer_size; + spa_ringbuffer_read_update(&s->dequeued_ring, s->buffer_index + 1); + pw_stream_queue_buffer(s->stream, s->buffer); + s->buffer = NULL; + return 0; } @@ -520,8 +573,7 @@ size_t pa_stream_writable_size(pa_stream *s) PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direction != PA_STREAM_RECORD, PA_ERR_BADSTATE, (size_t) -1); - pw_log_warn("Not Implemented"); - return 0; + return s->dequeued_size; } size_t pa_stream_readable_size(pa_stream *s) @@ -699,8 +751,23 @@ void pa_stream_set_buffer_attr_callback(pa_stream *s, pa_stream_notify_cb_t cb, s->buffer_attr_userdata = userdata; } +struct success_ack { + pa_stream_success_cb_t cb; + void *userdata; +}; + +static void on_success(pa_operation *o, void *userdata) +{ + struct success_ack *d = userdata; + if (d->cb) + d->cb(o->stream, PA_OK, d->userdata); +} + pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, void *userdata) { + pa_operation *o; + struct success_ack *d; + spa_assert(s); spa_assert(s->refcount >= 1); @@ -708,13 +775,21 @@ pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, voi PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); s->corked = b; - pw_log_warn("Not Implemented"); - return NULL; + pw_log_warn("Not Implemented"); + o = pa_operation_new(s->context, s, on_success, sizeof(struct success_ack)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + + return o; } pa_operation* pa_stream_flush(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) { + pa_operation *o; + struct success_ack *d; + spa_assert(s); spa_assert(s->refcount >= 1); @@ -722,24 +797,40 @@ pa_operation* pa_stream_flush(pa_stream *s, pa_stream_success_cb_t cb, void *use PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); pw_log_warn("Not Implemented"); - return NULL; + o = pa_operation_new(s->context, s, on_success, sizeof(struct success_ack)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + + return o; } pa_operation* pa_stream_prebuf(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) { + pa_operation *o; + struct success_ack *d; + spa_assert(s); spa_assert(s->refcount >= 1); - PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->buffer_attr.prebuf > 0, PA_ERR_BADSTATE); - return NULL; + pw_log_warn("Not Implemented"); + o = pa_operation_new(s->context, s, on_success, sizeof(struct success_ack)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + + return o; } pa_operation* pa_stream_trigger(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) { + pa_operation *o; + struct success_ack *d; + spa_assert(s); spa_assert(s->refcount >= 1); @@ -747,11 +838,20 @@ pa_operation* pa_stream_trigger(pa_stream *s, pa_stream_success_cb_t cb, void *u PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->buffer_attr.prebuf > 0, PA_ERR_BADSTATE); - return NULL; + pw_log_warn("Not Implemented"); + o = pa_operation_new(s->context, s, on_success, sizeof(struct success_ack)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + + return o; } pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_success_cb_t cb, void *userdata) { + pa_operation *o; + struct success_ack *d; + spa_assert(s); spa_assert(s->refcount >= 1); spa_assert(name); @@ -759,7 +859,13 @@ pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_succe PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); - return NULL; + pw_log_warn("Not Implemented"); + o = pa_operation_new(s->context, s, on_success, sizeof(struct success_ack)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + + return o; } int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) @@ -771,6 +877,8 @@ int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); PA_CHECK_VALIDITY(s->context, s->timing_info_valid, PA_ERR_NODATA); + pw_log_warn("Not Implemented"); + return 0; } @@ -784,6 +892,8 @@ int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative) PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); PA_CHECK_VALIDITY(s->context, s->timing_info_valid, PA_ERR_NODATA); + pw_log_warn("Not Implemented"); + return 0; } diff --git a/src/subscribe.c b/src/subscribe.c index de749b587..a78a4d009 100644 --- a/src/subscribe.c +++ b/src/subscribe.c @@ -23,13 +23,43 @@ #include "internal.h" +struct subscribe_data +{ + pa_context_success_cb_t cb; + void *userdata; +}; + +static void on_subscribed(pa_operation *o, void *userdata) +{ + struct subscribe_data *d = userdata; + if (d->cb) + d->cb(o->context, PA_OK, d->userdata); +} + pa_operation* pa_context_subscribe(pa_context *c, pa_subscription_mask_t m, pa_context_success_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); - return NULL; + pa_operation *o; + struct subscribe_data *d; + + pa_assert(c); + pa_assert(c->refcount >= 1); + + o = pa_operation_new(c, NULL, on_subscribed, sizeof(struct subscribe_data)); + d = o->userdata; + d->cb = cb; + d->userdata = userdata; + + return o; } void pa_context_set_subscribe_callback(pa_context *c, pa_context_subscribe_cb_t cb, void *userdata) { - pw_log_warn("Not Implemented"); + pa_assert(c); + pa_assert(c->refcount >= 1); + + if (c->state == PA_CONTEXT_TERMINATED || c->state == PA_CONTEXT_FAILED) + return; + + c->subscribe_callback = cb; + c->subscribe_userdata = userdata; } diff --git a/src/thread-mainloop.c b/src/thread-mainloop.c new file mode 100644 index 000000000..964f05272 --- /dev/null +++ b/src/thread-mainloop.c @@ -0,0 +1,118 @@ +/* PipeWire + * Copyright (C) 2018 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include + +#include +#include + +#include "internal.h" + +struct pa_threaded_mainloop +{ + pa_mainloop *loop; + struct pw_thread_loop *tloop; +}; + +pa_threaded_mainloop *pa_threaded_mainloop_new(void) +{ + pa_threaded_mainloop *m; + + m = calloc(1, sizeof(pa_threaded_mainloop)); + if (m == NULL) + return NULL; + + m->loop = pa_mainloop_new(); + if (m->loop == NULL) + goto no_mem; + + m->tloop = pw_thread_loop_new(m->loop->loop, NULL); + if (m->tloop == NULL) + goto no_mem; + + return m; + + no_mem: + if (m->loop) + pa_mainloop_free(m->loop); + free(m); + return NULL; +} + +void pa_threaded_mainloop_free(pa_threaded_mainloop* m) +{ + pw_thread_loop_destroy(m->tloop); + pa_mainloop_free(m->loop); + free(m); +} + +int pa_threaded_mainloop_start(pa_threaded_mainloop *m) +{ + return pw_thread_loop_start(m->tloop); +} + +void pa_threaded_mainloop_stop(pa_threaded_mainloop *m) +{ + pw_thread_loop_stop(m->tloop); +} + +void pa_threaded_mainloop_lock(pa_threaded_mainloop *m) +{ + pw_thread_loop_lock(m->tloop); +} + +void pa_threaded_mainloop_unlock(pa_threaded_mainloop *m) +{ + pw_thread_loop_unlock(m->tloop); +} + +void pa_threaded_mainloop_wait(pa_threaded_mainloop *m) +{ + pw_thread_loop_wait(m->tloop); +} + +void pa_threaded_mainloop_signal(pa_threaded_mainloop *m, int wait_for_accept) +{ + pw_thread_loop_signal(m->tloop, wait_for_accept); +} + +void pa_threaded_mainloop_accept(pa_threaded_mainloop *m) +{ + pw_thread_loop_accept(m->tloop); +} + +int pa_threaded_mainloop_get_retval(pa_threaded_mainloop *m) +{ + return pa_mainloop_get_retval(m->loop); +} + +pa_mainloop_api* pa_threaded_mainloop_get_api(pa_threaded_mainloop*m) +{ + return pa_mainloop_get_api(m->loop); +} + +int pa_threaded_mainloop_in_thread(pa_threaded_mainloop *m) +{ + return pw_thread_loop_in_thread(m->tloop); +} + +void pa_threaded_mainloop_set_name(pa_threaded_mainloop *m, const char *name) +{ +} diff --git a/src/timeval.c b/src/timeval.c new file mode 100644 index 000000000..dfdeb782d --- /dev/null +++ b/src/timeval.c @@ -0,0 +1,212 @@ +/*** + This file is part of PulseAudio. + + Copyright 2004-2006 Lennart Poettering + Copyright 2006 Pierre Ossman 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.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, see . +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#ifdef HAVE_WINDOWS_H +#include +#endif + +#include + +#include "internal.h" + +#define HAVE_GETTIMEOFDAY + +struct timeval *pa_gettimeofday(struct timeval *tv) { + pa_assert(tv); + +#if defined(OS_IS_WIN32) + /* + * Copied from implementation by Steven Edwards (LGPL). + * Found on wine mailing list. + */ +#if defined(_MSC_VER) || defined(__BORLANDC__) +#define EPOCHFILETIME (116444736000000000i64) +#else +#define EPOCHFILETIME (116444736000000000LL) +#endif +{ + FILETIME ft; + LARGE_INTEGER li; + int64_t t; + + GetSystemTimeAsFileTime(&ft); + li.LowPart = ft.dwLowDateTime; + li.HighPart = ft.dwHighDateTime; + t = li.QuadPart; /* In 100-nanosecond intervals */ + t -= EPOCHFILETIME; /* Offset to the Epoch time */ + t /= 10; /* In microseconds */ + tv->tv_sec = (time_t) (t / PA_USEC_PER_SEC); + tv->tv_usec = (suseconds_t) (t % PA_USEC_PER_SEC); +} +#elif defined(HAVE_GETTIMEOFDAY) + pa_assert_se(gettimeofday(tv, NULL) == 0); +#else +#error "Platform lacks gettimeofday() or equivalent function." +#endif + + return tv; +} + +pa_usec_t pa_timeval_diff(const struct timeval *a, const struct timeval *b) { + pa_usec_t r; + + pa_assert(a); + pa_assert(b); + + /* Check which is the earlier time and swap the two arguments if required. */ + if (PA_UNLIKELY(pa_timeval_cmp(a, b) < 0)) { + const struct timeval *c; + c = a; + a = b; + b = c; + } + + /* Calculate the second difference*/ + r = ((pa_usec_t) a->tv_sec - (pa_usec_t) b->tv_sec) * PA_USEC_PER_SEC; + + /* Calculate the microsecond difference */ + if (a->tv_usec > b->tv_usec) + r += (pa_usec_t) a->tv_usec - (pa_usec_t) b->tv_usec; + else if (a->tv_usec < b->tv_usec) + r -= (pa_usec_t) b->tv_usec - (pa_usec_t) a->tv_usec; + + return r; +} + +int pa_timeval_cmp(const struct timeval *a, const struct timeval *b) { + pa_assert(a); + pa_assert(b); + + if (a->tv_sec < b->tv_sec) + return -1; + + if (a->tv_sec > b->tv_sec) + return 1; + + if (a->tv_usec < b->tv_usec) + return -1; + + if (a->tv_usec > b->tv_usec) + return 1; + + return 0; +} + +pa_usec_t pa_timeval_age(const struct timeval *tv) { + struct timeval now; + pa_assert(tv); + + return pa_timeval_diff(pa_gettimeofday(&now), tv); +} + +struct timeval* pa_timeval_add(struct timeval *tv, pa_usec_t v) { + time_t secs; + pa_assert(tv); + + secs = (time_t) (v/PA_USEC_PER_SEC); + + if (PA_UNLIKELY(tv->tv_sec > PA_INT_TYPE_MAX(time_t) - secs)) + goto overflow; + + tv->tv_sec += secs; + v -= (pa_usec_t) secs * PA_USEC_PER_SEC; + tv->tv_usec += (suseconds_t) v; + + /* Normalize */ + while ((pa_usec_t) tv->tv_usec >= PA_USEC_PER_SEC) { + + if (PA_UNLIKELY(tv->tv_sec >= PA_INT_TYPE_MAX(time_t))) + goto overflow; + + tv->tv_sec++; + tv->tv_usec -= (suseconds_t) PA_USEC_PER_SEC; + } + + return tv; + +overflow: + tv->tv_sec = PA_INT_TYPE_MAX(time_t); + tv->tv_usec = (suseconds_t) (PA_USEC_PER_SEC-1); + return tv; +} + +struct timeval* pa_timeval_sub(struct timeval *tv, pa_usec_t v) { + time_t secs; + pa_assert(tv); + + secs = (time_t) (v/PA_USEC_PER_SEC); + + if (PA_UNLIKELY(tv->tv_sec < secs)) + goto underflow; + + tv->tv_sec -= secs; + v -= (pa_usec_t) secs * PA_USEC_PER_SEC; + + if (tv->tv_usec >= (suseconds_t) v) + tv->tv_usec -= (suseconds_t) v; + else { + + if (PA_UNLIKELY(tv->tv_sec <= 0)) + goto underflow; + + tv->tv_sec --; + tv->tv_usec += (suseconds_t) (PA_USEC_PER_SEC - v); + } + + return tv; + +underflow: + tv->tv_sec = 0; + tv->tv_usec = 0; + return tv; +} + +struct timeval* pa_timeval_store(struct timeval *tv, pa_usec_t v) { + pa_assert(tv); + + if (PA_UNLIKELY(v == PA_USEC_INVALID)) { + tv->tv_sec = PA_INT_TYPE_MAX(time_t); + tv->tv_usec = (suseconds_t) (PA_USEC_PER_SEC-1); + + return tv; + } + + tv->tv_sec = (time_t) (v / PA_USEC_PER_SEC); + tv->tv_usec = (suseconds_t) (v % PA_USEC_PER_SEC); + + return tv; +} + +pa_usec_t pa_timeval_load(const struct timeval *tv) { + + if (PA_UNLIKELY(!tv)) + return PA_USEC_INVALID; + + return + (pa_usec_t) tv->tv_sec * PA_USEC_PER_SEC + + (pa_usec_t) tv->tv_usec; +}