mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-11-03 09:01:54 -05:00 
			
		
		
		
	Various improvements
context: fix memory free subscribe: implement dummy methods introspect: implement more stream: keep track of dequeued buffers ourselves because we need to be able to cancel and keep track of writable size.
This commit is contained in:
		
							parent
							
								
									68643fd25a
								
							
						
					
					
						commit
						fe932db2c6
					
				
					 10 changed files with 804 additions and 57 deletions
				
			
		| 
						 | 
					@ -260,7 +260,6 @@ static void context_free(pa_context *c)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (c->proplist)
 | 
						if (c->proplist)
 | 
				
			||||||
		pa_proplist_free(c->proplist);
 | 
							pa_proplist_free(c->proplist);
 | 
				
			||||||
	free(c);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void pa_context_unref(pa_context *c)
 | 
					void pa_context_unref(pa_context *c)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										90
									
								
								src/ext-stream-restore.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								src/ext-stream-restore.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,90 @@
 | 
				
			||||||
 | 
					/* PipeWire
 | 
				
			||||||
 | 
					 * Copyright (C) 2018 Wim Taymans <wim.taymans@gmail.com>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * 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 <pipewire/pipewire.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <pulse/ext-stream-restore.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#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");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -24,11 +24,13 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <spa/utils/defs.h>
 | 
					#include <spa/utils/defs.h>
 | 
				
			||||||
#include <spa/utils/hook.h>
 | 
					#include <spa/utils/hook.h>
 | 
				
			||||||
 | 
					#include <spa/utils/ringbuffer.h>
 | 
				
			||||||
#include <spa/param/format-utils.h>
 | 
					#include <spa/param/format-utils.h>
 | 
				
			||||||
#include <spa/param/audio/format-utils.h>
 | 
					#include <spa/param/audio/format-utils.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <pulse/stream.h>
 | 
					#include <pulse/stream.h>
 | 
				
			||||||
#include <pulse/format.h>
 | 
					#include <pulse/format.h>
 | 
				
			||||||
 | 
					#include <pulse/subscribe.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <pipewire/utils.h>
 | 
					#include <pipewire/utils.h>
 | 
				
			||||||
#include <pipewire/interfaces.h>
 | 
					#include <pipewire/interfaces.h>
 | 
				
			||||||
| 
						 | 
					@ -44,13 +46,28 @@ extern "C" {
 | 
				
			||||||
#define pa_strneq(a,b,n)	(!strncmp((a),(b),(n)))
 | 
					#define pa_strneq(a,b,n)	(!strncmp((a),(b),(n)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define PA_UNLIKELY		SPA_UNLIKELY
 | 
					#define PA_UNLIKELY		SPA_UNLIKELY
 | 
				
			||||||
 | 
					#define PA_LIKELY		SPA_LIKELY
 | 
				
			||||||
#define PA_MIN			SPA_MIN
 | 
					#define PA_MIN			SPA_MIN
 | 
				
			||||||
#define PA_MAX			SPA_MAX
 | 
					#define PA_MAX			SPA_MAX
 | 
				
			||||||
#define pa_assert		spa_assert
 | 
					#define pa_assert		spa_assert
 | 
				
			||||||
 | 
					#define pa_assert_se		spa_assert
 | 
				
			||||||
#define pa_return_val_if_fail	spa_return_val_if_fail
 | 
					#define pa_return_val_if_fail	spa_return_val_if_fail
 | 
				
			||||||
#define pa_assert_not_reached	spa_assert_not_reached
 | 
					#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__
 | 
					#ifdef __GNUC__
 | 
				
			||||||
#define PA_CLAMP_UNLIKELY(x, low, high)                                 \
 | 
					#define PA_CLAMP_UNLIKELY(x, low, high)                                 \
 | 
				
			||||||
| 
						 | 
					@ -183,6 +200,9 @@ struct pa_context {
 | 
				
			||||||
	void *state_userdata;
 | 
						void *state_userdata;
 | 
				
			||||||
	pa_context_event_cb_t event_callback;
 | 
						pa_context_event_cb_t event_callback;
 | 
				
			||||||
	void *event_userdata;
 | 
						void *event_userdata;
 | 
				
			||||||
 | 
						pa_context_subscribe_cb_t subscribe_callback;
 | 
				
			||||||
 | 
						void *subscribe_userdata;
 | 
				
			||||||
 | 
						pa_subscription_mask_t subscribe_mask;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bool no_fail;
 | 
						bool no_fail;
 | 
				
			||||||
	uint32_t client_index;
 | 
						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);
 | 
						spa_type_audio_format_map(map, &type->audio_format);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MAX_BUFFERS     64
 | 
				
			||||||
 | 
					#define MASK_BUFFERS    (MAX_BUFFERS-1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct pa_stream {
 | 
					struct pa_stream {
 | 
				
			||||||
	struct spa_list link;
 | 
						struct spa_list link;
 | 
				
			||||||
	int refcount;
 | 
						int refcount;
 | 
				
			||||||
| 
						 | 
					@ -270,7 +293,17 @@ struct pa_stream {
 | 
				
			||||||
	pa_stream_notify_cb_t buffer_attr_callback;
 | 
						pa_stream_notify_cb_t buffer_attr_callback;
 | 
				
			||||||
	void *buffer_attr_userdata;
 | 
						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;
 | 
						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);
 | 
					void pa_stream_set_state(pa_stream *s, pa_stream_state_t st);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										195
									
								
								src/introspect.c
									
										
									
									
									
								
							
							
						
						
									
										195
									
								
								src/introspect.c
									
										
									
									
									
								
							| 
						 | 
					@ -25,12 +25,12 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "internal.h"
 | 
					#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)
 | 
					static void node_event_info(void *object, struct pw_node_info *info)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct global *g = object;
 | 
						struct global *g = object;
 | 
				
			||||||
	pw_log_debug("update");
 | 
						pw_log_debug("update %d", g->id);
 | 
				
			||||||
	g->info = pw_node_info_update(g->info, info);
 | 
						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)
 | 
					static void module_event_info(void *object, struct pw_module_info *info)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
        struct global *g = object;
 | 
					        struct global *g = object;
 | 
				
			||||||
	pw_log_debug("update");
 | 
						pw_log_debug("update %d", g->id);
 | 
				
			||||||
        g->info = pw_module_info_update(g->info, info);
 | 
					        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)
 | 
					static void client_event_info(void *object, struct pw_client_info *info)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
        struct global *g = object;
 | 
					        struct global *g = object;
 | 
				
			||||||
	pw_log_debug("update");
 | 
						pw_log_debug("update %d", g->id);
 | 
				
			||||||
        g->info = pw_client_info_update(g->info, info);
 | 
					        g->info = pw_client_info_update(g->info, info);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -91,6 +91,8 @@ static int ensure_global(pa_context *c, struct global *g)
 | 
				
			||||||
	else
 | 
						else
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pw_log_debug("bind %d", g->id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	g->proxy = pw_registry_proxy_bind(c->registry_proxy, g->id, g->type,
 | 
						g->proxy = pw_registry_proxy_bind(c->registry_proxy, g->id, g->type,
 | 
				
			||||||
                                      client_version, 0);
 | 
					                                      client_version, 0);
 | 
				
			||||||
	if (g->proxy == NULL)
 | 
						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;
 | 
						struct global *g;
 | 
				
			||||||
	spa_list_for_each(g, &c->globals, link) {
 | 
						spa_list_for_each(g, &c->globals, link) {
 | 
				
			||||||
		if (!filter(c, g))
 | 
							if (!filter(c, g, false))
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
		ensure_global(c, g);
 | 
							ensure_global(c, g);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -124,6 +126,8 @@ static void sink_callback(struct sink_data *d)
 | 
				
			||||||
	struct global *g = d->global;
 | 
						struct global *g = d->global;
 | 
				
			||||||
	struct pw_node_info *info = g->info;
 | 
						struct pw_node_info *info = g->info;
 | 
				
			||||||
	pa_sink_info i;
 | 
						pa_sink_info i;
 | 
				
			||||||
 | 
						pa_format_info ii[1];
 | 
				
			||||||
 | 
						pa_format_info *ip[1];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	spa_zero(i);
 | 
						spa_zero(i);
 | 
				
			||||||
	i.index = g->id;
 | 
						i.index = g->id;
 | 
				
			||||||
| 
						 | 
					@ -132,7 +136,11 @@ static void sink_callback(struct sink_data *d)
 | 
				
			||||||
	i.owner_module = g->parent_id;
 | 
						i.owner_module = g->parent_id;
 | 
				
			||||||
	i.base_volume = PA_VOLUME_NORM;
 | 
						i.base_volume = PA_VOLUME_NORM;
 | 
				
			||||||
	i.n_volume_steps = PA_VOLUME_NORM+1;
 | 
						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);
 | 
						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;
 | 
						struct sink_data *d = userdata;
 | 
				
			||||||
	sink_callback(d);
 | 
						sink_callback(d);
 | 
				
			||||||
	d->cb(d->context, NULL, 1, d->userdata);
 | 
						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;
 | 
						const char *str;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -158,10 +167,41 @@ static int sink_filter(pa_context *c, struct global *g)
 | 
				
			||||||
	return 1;
 | 
						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)
 | 
					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");
 | 
						pa_operation *o;
 | 
				
			||||||
	return NULL;
 | 
						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)
 | 
					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(c->refcount >= 1);
 | 
				
			||||||
	pa_assert(cb);
 | 
						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);
 | 
						PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if ((g = pa_context_find_global(c, idx)) == NULL)
 | 
						if ((g = pa_context_find_global(c, idx)) == NULL)
 | 
				
			||||||
		return NULL;
 | 
							return NULL;
 | 
				
			||||||
	if (!sink_filter(c, g))
 | 
						if (!sink_filter(c, g, false))
 | 
				
			||||||
		return NULL;
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ensure_global(c, g);
 | 
						ensure_global(c, g);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	o = pa_operation_new(c, NULL, sink_info, sizeof(struct sink_data));
 | 
						o = pa_operation_new(c, NULL, sink_info, sizeof(struct sink_data));
 | 
				
			||||||
	d = o->userdata;
 | 
						d = o->userdata;
 | 
				
			||||||
 | 
						d->context = c;
 | 
				
			||||||
 | 
						d->cb = cb;
 | 
				
			||||||
 | 
						d->userdata = userdata;
 | 
				
			||||||
	d->global = g;
 | 
						d->global = g;
 | 
				
			||||||
	return o;
 | 
						return o;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -196,12 +240,13 @@ static void sink_info_list(pa_operation *o, void *userdata)
 | 
				
			||||||
	struct global *g;
 | 
						struct global *g;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	spa_list_for_each(g, &c->globals, link) {
 | 
						spa_list_for_each(g, &c->globals, link) {
 | 
				
			||||||
		if (!sink_filter(c, g))
 | 
							if (!sink_filter(c, g, true))
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
		d->global = g;
 | 
							d->global = g;
 | 
				
			||||||
		sink_callback(d);
 | 
							sink_callback(d);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	d->cb(c, NULL, 1, d->userdata);
 | 
						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)
 | 
					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);
 | 
						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;
 | 
						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)
 | 
						if ((g = pa_context_find_global(c, idx)) == NULL)
 | 
				
			||||||
		return NULL;
 | 
							return NULL;
 | 
				
			||||||
	if (!source_filter(c, g))
 | 
						if (!source_filter(c, g, false))
 | 
				
			||||||
		return NULL;
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ensure_global(c, g);
 | 
						ensure_global(c, g);
 | 
				
			||||||
| 
						 | 
					@ -358,7 +403,7 @@ static void source_info_list(pa_operation *o, void *userdata)
 | 
				
			||||||
	struct global *g;
 | 
						struct global *g;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	spa_list_for_each(g, &c->globals, link) {
 | 
						spa_list_for_each(g, &c->globals, link) {
 | 
				
			||||||
		if (!source_filter(c, g))
 | 
							if (!source_filter(c, g, true))
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
		d->global = g;
 | 
							d->global = g;
 | 
				
			||||||
		source_callback(d);
 | 
							source_callback(d);
 | 
				
			||||||
| 
						 | 
					@ -471,7 +516,7 @@ static void module_info(pa_operation *o, void *userdata)
 | 
				
			||||||
	d->cb(d->context, NULL, 1, d->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)
 | 
						if (g->type != c->t->module)
 | 
				
			||||||
		return 0;
 | 
							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)
 | 
						if ((g = pa_context_find_global(c, idx)) == NULL)
 | 
				
			||||||
		return NULL;
 | 
							return NULL;
 | 
				
			||||||
	if (!module_filter(c, g))
 | 
						if (!module_filter(c, g, false))
 | 
				
			||||||
		return NULL;
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ensure_global(c, g);
 | 
						ensure_global(c, g);
 | 
				
			||||||
| 
						 | 
					@ -511,7 +556,7 @@ static void module_info_list(pa_operation *o, void *userdata)
 | 
				
			||||||
	struct global *g;
 | 
						struct global *g;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	spa_list_for_each(g, &c->globals, link) {
 | 
						spa_list_for_each(g, &c->globals, link) {
 | 
				
			||||||
		if (!module_filter(c, g))
 | 
							if (!module_filter(c, g, true))
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
		d->global = g;
 | 
							d->global = g;
 | 
				
			||||||
		module_callback(d);
 | 
							module_callback(d);
 | 
				
			||||||
| 
						 | 
					@ -583,7 +628,7 @@ static void client_info(pa_operation *o, void *userdata)
 | 
				
			||||||
	d->cb(d->context, NULL, 1, d->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)
 | 
						if (g->type != c->t->client)
 | 
				
			||||||
		return 0;
 | 
							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)
 | 
						if ((g = pa_context_find_global(c, idx)) == NULL)
 | 
				
			||||||
		return NULL;
 | 
							return NULL;
 | 
				
			||||||
	if (!client_filter(c, g))
 | 
						if (!client_filter(c, g, false))
 | 
				
			||||||
		return NULL;
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ensure_global(c, g);
 | 
						ensure_global(c, g);
 | 
				
			||||||
| 
						 | 
					@ -623,7 +668,7 @@ static void client_info_list(pa_operation *o, void *userdata)
 | 
				
			||||||
	struct global *g;
 | 
						struct global *g;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	spa_list_for_each(g, &c->globals, link) {
 | 
						spa_list_for_each(g, &c->globals, link) {
 | 
				
			||||||
		if (!client_filter(c, g))
 | 
							if (!client_filter(c, g, true))
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
		d->global = g;
 | 
							d->global = g;
 | 
				
			||||||
		client_callback(d);
 | 
							client_callback(d);
 | 
				
			||||||
| 
						 | 
					@ -694,16 +739,118 @@ pa_operation* pa_context_set_port_latency_offset(pa_context *c, const char *card
 | 
				
			||||||
	return NULL;
 | 
						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)
 | 
					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");
 | 
						pa_operation *o;
 | 
				
			||||||
	return NULL;
 | 
						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)
 | 
					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");
 | 
						pa_operation *o;
 | 
				
			||||||
	return NULL;
 | 
						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)
 | 
					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)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,6 +7,7 @@ pipewire_pulseaudio_sources = [
 | 
				
			||||||
  'error.c',
 | 
					  'error.c',
 | 
				
			||||||
  'ext-device-manager.c',
 | 
					  'ext-device-manager.c',
 | 
				
			||||||
  'ext-device-restore.c',
 | 
					  'ext-device-restore.c',
 | 
				
			||||||
 | 
					  'ext-stream-restore.c',
 | 
				
			||||||
  'format.c',
 | 
					  'format.c',
 | 
				
			||||||
  'introspect.c',
 | 
					  'introspect.c',
 | 
				
			||||||
  'json.c',
 | 
					  'json.c',
 | 
				
			||||||
| 
						 | 
					@ -20,6 +21,8 @@ pipewire_pulseaudio_sources = [
 | 
				
			||||||
  'stream.c',
 | 
					  'stream.c',
 | 
				
			||||||
  'strbuf.c',
 | 
					  'strbuf.c',
 | 
				
			||||||
  'subscribe.c',
 | 
					  'subscribe.c',
 | 
				
			||||||
 | 
					  'thread-mainloop.c',
 | 
				
			||||||
 | 
					  'timeval.c',
 | 
				
			||||||
  'utf8.c',
 | 
					  'utf8.c',
 | 
				
			||||||
  'util.c',
 | 
					  'util.c',
 | 
				
			||||||
  'version.c',
 | 
					  'version.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, ...)
 | 
					int pa_proplist_setf(pa_proplist *p, const char *key, const char *format, ...)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	pw_log_warn("Not Implemented");
 | 
						va_list varargs;
 | 
				
			||||||
	return -1;
 | 
					
 | 
				
			||||||
 | 
						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)
 | 
					int pa_proplist_set(pa_proplist *p, const char *key, const void *data, size_t nbytes)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										162
									
								
								src/stream.c
									
										
									
									
									
								
							
							
						
						
									
										162
									
								
								src/stream.c
									
										
									
									
									
								
							| 
						 | 
					@ -23,6 +23,7 @@
 | 
				
			||||||
#include <spa/utils/defs.h>
 | 
					#include <spa/utils/defs.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <pulse/stream.h>
 | 
					#include <pulse/stream.h>
 | 
				
			||||||
 | 
					#include <pulse/timeval.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <pipewire/stream.h>
 | 
					#include <pipewire/stream.h>
 | 
				
			||||||
#include "internal.h"
 | 
					#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)
 | 
					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)
 | 
					static void stream_process(void *data)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	pa_stream *s = data;
 | 
						pa_stream *s = data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (s->direction == PA_STREAM_PLAYBACK) {
 | 
						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)
 | 
							if (s->write_callback)
 | 
				
			||||||
			s->write_callback(s, 4096, s->write_userdata);
 | 
								s->write_callback(s, s->dequeued_size, s->write_userdata);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	else {
 | 
						else {
 | 
				
			||||||
		if (s->read_callback)
 | 
							if (s->read_callback)
 | 
				
			||||||
| 
						 | 
					@ -159,6 +180,8 @@ pa_stream* stream_new(pa_context *c, const char *name,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	s->timing_info_valid = false;
 | 
						s->timing_info_valid = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spa_ringbuffer_init(&s->dequeued_ring);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	spa_list_append(&c->streams, &s->link);
 | 
						spa_list_append(&c->streams, &s->link);
 | 
				
			||||||
	pa_stream_ref(s);
 | 
						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.channels,   "i", 2,
 | 
				
			||||||
                ":", s->type.format_audio.rate,       "i", 44100);
 | 
					                ":", 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,
 | 
						res = pw_stream_connect(s->stream,
 | 
				
			||||||
				direction == PA_STREAM_PLAYBACK ?
 | 
									direction == PA_STREAM_PLAYBACK ?
 | 
				
			||||||
					PW_DIRECTION_OUTPUT :
 | 
										PW_DIRECTION_OUTPUT :
 | 
				
			||||||
| 
						 | 
					@ -389,14 +418,23 @@ int pa_stream_connect_record(
 | 
				
			||||||
	return create_stream(PA_STREAM_RECORD, s, dev, attr, flags, NULL, NULL);
 | 
						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)
 | 
					int pa_stream_disconnect(pa_stream *s)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						pa_operation *o;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	spa_assert(s);
 | 
						spa_assert(s);
 | 
				
			||||||
	spa_assert(s->refcount >= 1);
 | 
						spa_assert(s->refcount >= 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	PA_CHECK_VALIDITY(s->context, s->context->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
 | 
						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;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -405,6 +443,9 @@ int pa_stream_begin_write(
 | 
				
			||||||
        void **data,
 | 
					        void **data,
 | 
				
			||||||
        size_t *nbytes)
 | 
					        size_t *nbytes)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						int32_t avail;
 | 
				
			||||||
 | 
						uint32_t index;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	spa_assert(s);
 | 
						spa_assert(s);
 | 
				
			||||||
	spa_assert(s->refcount >= 1);
 | 
						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);
 | 
						PA_CHECK_VALIDITY(s->context, nbytes && *nbytes != 0, PA_ERR_INVALID);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (s->buffer == NULL) {
 | 
						if (s->buffer == NULL) {
 | 
				
			||||||
		s->buffer = pw_stream_dequeue_buffer(s->stream);
 | 
							if ((avail = spa_ringbuffer_get_read_index(&s->dequeued_ring, &index)) <= 0) {
 | 
				
			||||||
	}
 | 
								*data = NULL;
 | 
				
			||||||
	if (s->buffer == NULL) {
 | 
								*nbytes = 0;
 | 
				
			||||||
		*data = NULL;
 | 
								return 0;
 | 
				
			||||||
		*nbytes = 0;
 | 
							}
 | 
				
			||||||
	}
 | 
							s->buffer = s->dequeued[index & MASK_BUFFERS];
 | 
				
			||||||
	else {
 | 
							s->buffer_index = index;
 | 
				
			||||||
		*data = s->buffer->buffer->datas[0].data;
 | 
							s->buffer_data = s->buffer->buffer->datas[0].data;
 | 
				
			||||||
		*nbytes = s->buffer->buffer->datas[0].maxsize;
 | 
							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;
 | 
						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->state == PA_STREAM_READY, PA_ERR_BADSTATE);
 | 
				
			||||||
	PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_PLAYBACK ||
 | 
						PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_PLAYBACK ||
 | 
				
			||||||
			s->direction == PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
 | 
								s->direction == PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
 | 
				
			||||||
	pw_log_warn("Not Implemented");
 | 
					
 | 
				
			||||||
 | 
						s->buffer = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -475,11 +523,16 @@ int pa_stream_write_ext_free(pa_stream *s,
 | 
				
			||||||
		pw_log_warn("Not Implemented");
 | 
							pw_log_warn("Not Implemented");
 | 
				
			||||||
		return PA_ERR_INVALID;
 | 
							return PA_ERR_INVALID;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	else {
 | 
					
 | 
				
			||||||
		s->buffer->buffer->datas[0].chunk->offset = 0;
 | 
						s->buffer->buffer->datas[0].chunk->offset = 0;
 | 
				
			||||||
		s->buffer->buffer->datas[0].chunk->size = nbytes;
 | 
						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);
 | 
						pw_stream_queue_buffer(s->stream, s->buffer);
 | 
				
			||||||
 | 
						s->buffer = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						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_CHECK_VALIDITY_RETURN_ANY(s->context, s->direction != PA_STREAM_RECORD,
 | 
				
			||||||
			PA_ERR_BADSTATE, (size_t) -1);
 | 
								PA_ERR_BADSTATE, (size_t) -1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pw_log_warn("Not Implemented");
 | 
						return s->dequeued_size;
 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
size_t pa_stream_readable_size(pa_stream *s)
 | 
					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;
 | 
						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* 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);
 | 
				
			||||||
	spa_assert(s->refcount >= 1);
 | 
						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);
 | 
						PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	s->corked = b;
 | 
						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* 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);
 | 
				
			||||||
	spa_assert(s->refcount >= 1);
 | 
						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);
 | 
						PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pw_log_warn("Not Implemented");
 | 
						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* 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);
 | 
				
			||||||
	spa_assert(s->refcount >= 1);
 | 
						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->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->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE);
 | 
				
			||||||
	PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->buffer_attr.prebuf > 0, 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* 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);
 | 
				
			||||||
	spa_assert(s->refcount >= 1);
 | 
						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->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE);
 | 
				
			||||||
	PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->buffer_attr.prebuf > 0, 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* 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);
 | 
				
			||||||
	spa_assert(s->refcount >= 1);
 | 
						spa_assert(s->refcount >= 1);
 | 
				
			||||||
	spa_assert(name);
 | 
						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->state == PA_STREAM_READY, PA_ERR_BADSTATE);
 | 
				
			||||||
	PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, 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)
 | 
					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->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
 | 
				
			||||||
	PA_CHECK_VALIDITY(s->context, s->timing_info_valid, PA_ERR_NODATA);
 | 
						PA_CHECK_VALIDITY(s->context, s->timing_info_valid, PA_ERR_NODATA);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pw_log_warn("Not Implemented");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						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->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
 | 
				
			||||||
	PA_CHECK_VALIDITY(s->context, s->timing_info_valid, PA_ERR_NODATA);
 | 
						PA_CHECK_VALIDITY(s->context, s->timing_info_valid, PA_ERR_NODATA);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pw_log_warn("Not Implemented");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -23,13 +23,43 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "internal.h"
 | 
					#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)
 | 
					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");
 | 
						pa_operation *o;
 | 
				
			||||||
	return NULL;
 | 
						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)
 | 
					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;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										118
									
								
								src/thread-mainloop.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								src/thread-mainloop.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,118 @@
 | 
				
			||||||
 | 
					/* PipeWire
 | 
				
			||||||
 | 
					 * Copyright (C) 2018 Wim Taymans <wim.taymans@gmail.com>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * 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 <pipewire/log.h>
 | 
				
			||||||
 | 
					#include <pipewire/thread-loop.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <pulse/mainloop.h>
 | 
				
			||||||
 | 
					#include <pulse/thread-mainloop.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#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)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										212
									
								
								src/timeval.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										212
									
								
								src/timeval.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,212 @@
 | 
				
			||||||
 | 
					/***
 | 
				
			||||||
 | 
					  This file is part of PulseAudio.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Copyright 2004-2006 Lennart Poettering
 | 
				
			||||||
 | 
					  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.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 <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					***/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef HAVE_CONFIG_H
 | 
				
			||||||
 | 
					#include <config.h>
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <stddef.h>
 | 
				
			||||||
 | 
					#include <sys/time.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef HAVE_WINDOWS_H
 | 
				
			||||||
 | 
					#include <windows.h>
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <pulse/timeval.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#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;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue