2004-07-16 19:56:36 +00:00
|
|
|
/***
|
2006-06-19 21:53:48 +00:00
|
|
|
This file is part of PulseAudio.
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-02-13 15:35:19 +00:00
|
|
|
Copyright 2004-2006 Lennart Poettering
|
|
|
|
|
Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
|
|
|
|
|
|
2006-06-19 21:53:48 +00:00
|
|
|
PulseAudio is free software; you can redistribute it and/or modify
|
2004-11-14 14:58:54 +00:00
|
|
|
it under the terms of the GNU Lesser General Public License as published
|
2009-03-03 20:23:02 +00:00
|
|
|
by the Free Software Foundation; either version 2.1 of the License,
|
2004-07-16 19:56:36 +00:00
|
|
|
or (at your option) any later version.
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-06-19 21:53:48 +00:00
|
|
|
PulseAudio is distributed in the hope that it will be useful, but
|
2004-07-16 19:56:36 +00:00
|
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
|
General Public License for more details.
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-11-14 14:58:54 +00:00
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
2014-11-26 14:14:51 +01:00
|
|
|
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
|
2004-07-16 19:56:36 +00:00
|
|
|
***/
|
|
|
|
|
|
2004-07-16 19:16:42 +00:00
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
|
#include <config.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
2004-07-06 00:08:44 +00:00
|
|
|
#include <string.h>
|
2004-06-20 01:12:13 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
2006-02-24 15:12:42 +00:00
|
|
|
#include <unistd.h>
|
2004-06-20 01:12:13 +00:00
|
|
|
|
2009-04-05 02:13:43 +03:00
|
|
|
#include <pulse/rtclock.h>
|
2006-06-19 21:53:48 +00:00
|
|
|
#include <pulse/timeval.h>
|
|
|
|
|
#include <pulse/version.h>
|
|
|
|
|
#include <pulse/utf8.h>
|
|
|
|
|
#include <pulse/util.h>
|
|
|
|
|
#include <pulse/xmalloc.h>
|
2011-03-02 11:31:51 +05:30
|
|
|
#include <pulse/internal.h>
|
2006-03-02 21:56:15 +00:00
|
|
|
|
2006-06-19 21:53:48 +00:00
|
|
|
#include <pulsecore/native-common.h>
|
|
|
|
|
#include <pulsecore/packet.h>
|
|
|
|
|
#include <pulsecore/client.h>
|
|
|
|
|
#include <pulsecore/source-output.h>
|
|
|
|
|
#include <pulsecore/sink-input.h>
|
|
|
|
|
#include <pulsecore/pstream.h>
|
|
|
|
|
#include <pulsecore/tagstruct.h>
|
|
|
|
|
#include <pulsecore/pdispatch.h>
|
|
|
|
|
#include <pulsecore/pstream-util.h>
|
|
|
|
|
#include <pulsecore/namereg.h>
|
|
|
|
|
#include <pulsecore/core-scache.h>
|
|
|
|
|
#include <pulsecore/core-subscribe.h>
|
2020-01-14 10:15:36 +01:00
|
|
|
#include <pulsecore/message-handler.h>
|
2006-06-19 21:53:48 +00:00
|
|
|
#include <pulsecore/log.h>
|
2016-04-15 23:06:02 +02:00
|
|
|
#include <pulsecore/mem.h>
|
2006-06-19 21:53:48 +00:00
|
|
|
#include <pulsecore/strlist.h>
|
2008-08-01 02:10:54 +03:00
|
|
|
#include <pulsecore/shared.h>
|
2006-06-19 21:53:48 +00:00
|
|
|
#include <pulsecore/sample-util.h>
|
2006-07-19 21:48:35 +00:00
|
|
|
#include <pulsecore/creds.h>
|
|
|
|
|
#include <pulsecore/core-util.h>
|
2006-07-20 18:43:20 +00:00
|
|
|
#include <pulsecore/ipacl.h>
|
2007-10-28 19:13:50 +00:00
|
|
|
#include <pulsecore/thread-mq.h>
|
2016-03-13 00:57:06 +02:00
|
|
|
#include <pulsecore/mem.h>
|
2006-02-17 12:10:58 +00:00
|
|
|
|
2004-06-08 23:54:24 +00:00
|
|
|
#include "protocol-native.h"
|
|
|
|
|
|
2011-11-10 12:43:09 +05:30
|
|
|
/* #define PROTOCOL_NATIVE_DEBUG */
|
|
|
|
|
|
2004-11-18 00:28:26 +00:00
|
|
|
/* Kick a client if it doesn't authenticate within this time */
|
2009-04-05 02:13:43 +03:00
|
|
|
#define AUTH_TIMEOUT (60 * PA_USEC_PER_SEC)
|
2004-11-18 00:28:26 +00:00
|
|
|
|
|
|
|
|
/* Don't accept more connection than this */
|
2006-06-21 16:36:58 +00:00
|
|
|
#define MAX_CONNECTIONS 64
|
2004-11-18 00:28:26 +00:00
|
|
|
|
2006-02-22 21:02:27 +00:00
|
|
|
#define MAX_MEMBLOCKQ_LENGTH (4*1024*1024) /* 4MB */
|
2008-05-15 23:34:41 +00:00
|
|
|
#define DEFAULT_TLENGTH_MSEC 2000 /* 2s */
|
|
|
|
|
#define DEFAULT_PROCESS_MSEC 20 /* 20ms */
|
|
|
|
|
#define DEFAULT_FRAGSIZE_MSEC DEFAULT_TLENGTH_MSEC
|
2006-02-22 21:02:27 +00:00
|
|
|
|
2008-08-03 16:44:38 +02:00
|
|
|
struct pa_native_protocol;
|
2004-06-20 01:12:13 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
typedef struct record_stream {
|
|
|
|
|
pa_msgobject parent;
|
|
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *connection;
|
2004-06-20 01:12:13 +00:00
|
|
|
uint32_t index;
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_source_output *source_output;
|
|
|
|
|
pa_memblockq *memblockq;
|
2009-03-30 18:46:12 +02:00
|
|
|
|
2013-06-27 19:28:09 +02:00
|
|
|
bool adjust_latency:1;
|
|
|
|
|
bool early_requests:1;
|
2009-03-30 18:46:12 +02:00
|
|
|
|
2011-06-14 16:38:02 -07:00
|
|
|
/* Requested buffer attributes */
|
|
|
|
|
pa_buffer_attr buffer_attr_req;
|
|
|
|
|
/* Fixed-up and adjusted buffer attributes */
|
2009-03-30 18:46:12 +02:00
|
|
|
pa_buffer_attr buffer_attr;
|
2009-04-05 02:59:02 +02:00
|
|
|
|
|
|
|
|
pa_atomic_t on_the_fly;
|
|
|
|
|
pa_usec_t configured_source_latency;
|
|
|
|
|
size_t drop_initial;
|
|
|
|
|
|
|
|
|
|
/* Only updated after SOURCE_OUTPUT_MESSAGE_UPDATE_LATENCY */
|
|
|
|
|
size_t on_the_fly_snapshot;
|
|
|
|
|
pa_usec_t current_monitor_latency;
|
|
|
|
|
pa_usec_t current_source_latency;
|
2007-10-28 19:13:50 +00:00
|
|
|
} record_stream;
|
|
|
|
|
|
2008-08-03 16:44:38 +02:00
|
|
|
#define RECORD_STREAM(o) (record_stream_cast(o))
|
2009-08-21 21:27:44 +02:00
|
|
|
PA_DEFINE_PRIVATE_CLASS(record_stream, pa_msgobject);
|
2008-08-03 16:44:38 +02:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
typedef struct output_stream {
|
|
|
|
|
pa_msgobject parent;
|
|
|
|
|
} output_stream;
|
2004-06-20 01:12:13 +00:00
|
|
|
|
2008-08-03 16:44:38 +02:00
|
|
|
#define OUTPUT_STREAM(o) (output_stream_cast(o))
|
2009-08-21 21:27:44 +02:00
|
|
|
PA_DEFINE_PRIVATE_CLASS(output_stream, pa_msgobject);
|
2008-08-03 16:44:38 +02:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
typedef struct playback_stream {
|
|
|
|
|
output_stream parent;
|
|
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *connection;
|
2004-06-20 01:12:13 +00:00
|
|
|
uint32_t index;
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_sink_input *sink_input;
|
|
|
|
|
pa_memblockq *memblockq;
|
2009-03-30 18:46:12 +02:00
|
|
|
|
2013-06-27 19:28:09 +02:00
|
|
|
bool adjust_latency:1;
|
|
|
|
|
bool early_requests:1;
|
2009-03-30 18:46:12 +02:00
|
|
|
|
2013-06-27 19:28:09 +02:00
|
|
|
bool is_underrun:1;
|
|
|
|
|
bool drain_request:1;
|
2004-07-07 22:02:15 +00:00
|
|
|
uint32_t drain_tag;
|
2006-02-20 04:05:16 +00:00
|
|
|
uint32_t syncid;
|
|
|
|
|
|
2010-12-09 11:08:37 +01:00
|
|
|
/* Optimization to avoid too many rewinds with a lot of small blocks */
|
|
|
|
|
pa_atomic_t seek_or_post_in_queue;
|
|
|
|
|
int64_t seek_windex;
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_atomic_t missing;
|
2009-04-05 02:59:02 +02:00
|
|
|
pa_usec_t configured_sink_latency;
|
2011-06-14 16:38:02 -07:00
|
|
|
/* Requested buffer attributes */
|
|
|
|
|
pa_buffer_attr buffer_attr_req;
|
|
|
|
|
/* Fixed-up and adjusted buffer attributes */
|
2009-03-30 18:46:12 +02:00
|
|
|
pa_buffer_attr buffer_attr;
|
2007-10-28 19:13:50 +00:00
|
|
|
|
|
|
|
|
/* Only updated after SINK_INPUT_MESSAGE_UPDATE_LATENCY */
|
|
|
|
|
int64_t read_index, write_index;
|
2008-05-15 23:34:41 +00:00
|
|
|
size_t render_memblockq_length;
|
2009-04-05 02:59:02 +02:00
|
|
|
pa_usec_t current_sink_latency;
|
|
|
|
|
uint64_t playing_for, underrun_for;
|
2007-10-28 19:13:50 +00:00
|
|
|
} playback_stream;
|
2004-06-08 23:54:24 +00:00
|
|
|
|
2008-08-03 16:44:38 +02:00
|
|
|
#define PLAYBACK_STREAM(o) (playback_stream_cast(o))
|
2009-08-21 21:27:44 +02:00
|
|
|
PA_DEFINE_PRIVATE_CLASS(playback_stream, output_stream);
|
2008-08-03 16:44:38 +02:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
typedef struct upload_stream {
|
|
|
|
|
output_stream parent;
|
|
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *connection;
|
2004-08-03 19:26:56 +00:00
|
|
|
uint32_t index;
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_memchunk memchunk;
|
2004-08-03 19:26:56 +00:00
|
|
|
size_t length;
|
|
|
|
|
char *name;
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_sample_spec sample_spec;
|
2006-01-27 16:25:31 +00:00
|
|
|
pa_channel_map channel_map;
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_proplist *proplist;
|
2007-10-28 19:13:50 +00:00
|
|
|
} upload_stream;
|
2004-08-03 19:26:56 +00:00
|
|
|
|
2008-08-03 16:44:38 +02:00
|
|
|
#define UPLOAD_STREAM(o) (upload_stream_cast(o))
|
2009-08-21 21:27:44 +02:00
|
|
|
PA_DEFINE_PRIVATE_CLASS(upload_stream, output_stream);
|
2008-08-03 16:44:38 +02:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
struct pa_native_connection {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_msgobject parent;
|
2008-08-03 16:44:38 +02:00
|
|
|
pa_native_protocol *protocol;
|
|
|
|
|
pa_native_options *options;
|
2013-06-27 19:28:09 +02:00
|
|
|
bool authorized:1;
|
|
|
|
|
bool is_local:1;
|
2006-03-02 21:56:15 +00:00
|
|
|
uint32_t version;
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_client *client;
|
2016-03-13 00:53:34 +02:00
|
|
|
/* R/W mempool, one per client connection, for srbchannel transport.
|
|
|
|
|
* Both server and client can write to this shm area.
|
|
|
|
|
*
|
|
|
|
|
* Note: This will be NULL if our connection with the client does
|
|
|
|
|
* not support srbchannels */
|
|
|
|
|
pa_mempool *rw_mempool;
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_pstream *pstream;
|
|
|
|
|
pa_pdispatch *pdispatch;
|
|
|
|
|
pa_idxset *record_streams, *output_streams;
|
2004-07-10 16:50:22 +00:00
|
|
|
uint32_t rrobin_index;
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_subscription *subscription;
|
|
|
|
|
pa_time_event *auth_timeout_event;
|
2014-04-25 17:57:18 +02:00
|
|
|
pa_srbchannel *srbpending;
|
2004-06-20 01:12:13 +00:00
|
|
|
};
|
2004-06-08 23:54:24 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
#define PA_NATIVE_CONNECTION(o) (pa_native_connection_cast(o))
|
2009-08-21 21:27:44 +02:00
|
|
|
PA_DEFINE_PRIVATE_CLASS(pa_native_connection, pa_msgobject);
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2008-08-03 16:44:38 +02:00
|
|
|
struct pa_native_protocol {
|
|
|
|
|
PA_REFCNT_DECLARE;
|
|
|
|
|
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_core *core;
|
|
|
|
|
pa_idxset *connections;
|
2008-08-03 16:44:38 +02:00
|
|
|
|
|
|
|
|
pa_strlist *servers;
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_hook hooks[PA_NATIVE_HOOK_MAX];
|
2008-08-03 16:44:38 +02:00
|
|
|
|
2008-08-03 18:56:10 +02:00
|
|
|
pa_hashmap *extensions;
|
2004-06-20 01:12:13 +00:00
|
|
|
};
|
|
|
|
|
|
2009-04-05 02:59:02 +02:00
|
|
|
enum {
|
|
|
|
|
SOURCE_OUTPUT_MESSAGE_UPDATE_LATENCY = PA_SOURCE_OUTPUT_MESSAGE_MAX
|
|
|
|
|
};
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
enum {
|
|
|
|
|
SINK_INPUT_MESSAGE_POST_DATA = PA_SINK_INPUT_MESSAGE_MAX, /* data from main loop to sink input */
|
|
|
|
|
SINK_INPUT_MESSAGE_DRAIN, /* disabled prebuf, get playback started. */
|
|
|
|
|
SINK_INPUT_MESSAGE_FLUSH,
|
|
|
|
|
SINK_INPUT_MESSAGE_TRIGGER,
|
|
|
|
|
SINK_INPUT_MESSAGE_SEEK,
|
|
|
|
|
SINK_INPUT_MESSAGE_PREBUF_FORCE,
|
2009-03-30 18:46:12 +02:00
|
|
|
SINK_INPUT_MESSAGE_UPDATE_LATENCY,
|
|
|
|
|
SINK_INPUT_MESSAGE_UPDATE_BUFFER_ATTR
|
2007-10-28 19:13:50 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
enum {
|
|
|
|
|
PLAYBACK_STREAM_MESSAGE_REQUEST_DATA, /* data requested from sink input from the main loop */
|
|
|
|
|
PLAYBACK_STREAM_MESSAGE_UNDERFLOW,
|
|
|
|
|
PLAYBACK_STREAM_MESSAGE_OVERFLOW,
|
2008-05-15 23:34:41 +00:00
|
|
|
PLAYBACK_STREAM_MESSAGE_DRAIN_ACK,
|
2009-03-30 18:46:12 +02:00
|
|
|
PLAYBACK_STREAM_MESSAGE_STARTED,
|
|
|
|
|
PLAYBACK_STREAM_MESSAGE_UPDATE_TLENGTH
|
2007-10-28 19:13:50 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
enum {
|
|
|
|
|
RECORD_STREAM_MESSAGE_POST_DATA /* data from source output to main loop */
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
enum {
|
|
|
|
|
CONNECTION_MESSAGE_RELEASE,
|
|
|
|
|
CONNECTION_MESSAGE_REVOKE
|
|
|
|
|
};
|
|
|
|
|
|
2013-03-01 11:51:54 +01:00
|
|
|
static bool sink_input_process_underrun_cb(pa_sink_input *i);
|
2008-05-15 23:34:41 +00:00
|
|
|
static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk);
|
2006-01-11 01:17:39 +00:00
|
|
|
static void sink_input_kill_cb(pa_sink_input *i);
|
2019-02-12 20:21:01 +01:00
|
|
|
static void sink_input_suspend_cb(pa_sink_input *i, pa_sink_state_t old_state, pa_suspend_cause_t old_suspend_cause);
|
2009-04-01 03:04:39 +02:00
|
|
|
static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest);
|
2008-05-15 23:34:41 +00:00
|
|
|
static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes);
|
|
|
|
|
static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes);
|
2008-06-28 01:09:07 +02:00
|
|
|
static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes);
|
2009-02-12 03:18:05 +01:00
|
|
|
static void sink_input_send_event_cb(pa_sink_input *i, const char *event, pa_proplist *pl);
|
2004-06-23 23:17:30 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
static void native_connection_send_memblock(pa_native_connection *c);
|
|
|
|
|
static void playback_stream_request_bytes(struct playback_stream*s);
|
2004-06-23 23:17:30 +00:00
|
|
|
|
2006-01-11 01:17:39 +00:00
|
|
|
static void source_output_kill_cb(pa_source_output *o);
|
|
|
|
|
static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk);
|
2019-02-12 20:21:01 +01:00
|
|
|
static void source_output_suspend_cb(pa_source_output *o, pa_source_state_t old_state, pa_suspend_cause_t old_suspend_cause);
|
2009-04-01 03:04:39 +02:00
|
|
|
static void source_output_moving_cb(pa_source_output *o, pa_source *dest);
|
2006-01-11 01:17:39 +00:00
|
|
|
static pa_usec_t source_output_get_latency_cb(pa_source_output *o);
|
2009-02-12 03:18:05 +01:00
|
|
|
static void source_output_send_event_cb(pa_source_output *o, const char *event, pa_proplist *pl);
|
2006-01-11 01:17:39 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
|
2009-04-05 02:59:02 +02:00
|
|
|
static int source_output_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2004-06-23 23:17:30 +00:00
|
|
|
/* structure management */
|
|
|
|
|
|
2009-03-30 18:26:23 +02:00
|
|
|
/* Called from main context */
|
2007-10-28 19:13:50 +00:00
|
|
|
static void upload_stream_unlink(upload_stream *s) {
|
|
|
|
|
pa_assert(s);
|
|
|
|
|
|
|
|
|
|
if (!s->connection)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
pa_assert_se(pa_idxset_remove_by_data(s->connection->output_streams, s, NULL) == s);
|
|
|
|
|
s->connection = NULL;
|
|
|
|
|
upload_stream_unref(s);
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-30 18:26:23 +02:00
|
|
|
/* Called from main context */
|
2007-10-28 19:13:50 +00:00
|
|
|
static void upload_stream_free(pa_object *o) {
|
|
|
|
|
upload_stream *s = UPLOAD_STREAM(o);
|
|
|
|
|
pa_assert(s);
|
|
|
|
|
|
|
|
|
|
upload_stream_unlink(s);
|
|
|
|
|
|
|
|
|
|
pa_xfree(s->name);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (s->proplist)
|
|
|
|
|
pa_proplist_free(s->proplist);
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
if (s->memchunk.memblock)
|
|
|
|
|
pa_memblock_unref(s->memchunk.memblock);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_xfree(s);
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-30 18:26:23 +02:00
|
|
|
/* Called from main context */
|
2007-10-28 19:13:50 +00:00
|
|
|
static upload_stream* upload_stream_new(
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c,
|
2007-10-28 19:13:50 +00:00
|
|
|
const pa_sample_spec *ss,
|
|
|
|
|
const pa_channel_map *map,
|
2008-05-15 23:34:41 +00:00
|
|
|
const char *name,
|
|
|
|
|
size_t length,
|
|
|
|
|
pa_proplist *p) {
|
2007-10-28 19:13:50 +00:00
|
|
|
|
|
|
|
|
upload_stream *s;
|
|
|
|
|
|
|
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(ss);
|
|
|
|
|
pa_assert(name);
|
|
|
|
|
pa_assert(length > 0);
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_assert(p);
|
2007-10-28 19:13:50 +00:00
|
|
|
|
|
|
|
|
s = pa_msgobject_new(upload_stream);
|
|
|
|
|
s->parent.parent.parent.free = upload_stream_free;
|
2004-08-03 19:26:56 +00:00
|
|
|
s->connection = c;
|
|
|
|
|
s->sample_spec = *ss;
|
2006-01-27 16:25:31 +00:00
|
|
|
s->channel_map = *map;
|
2004-08-04 16:39:30 +00:00
|
|
|
s->name = pa_xstrdup(name);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_memchunk_reset(&s->memchunk);
|
2004-08-03 19:26:56 +00:00
|
|
|
s->length = length;
|
2008-05-15 23:34:41 +00:00
|
|
|
s->proplist = pa_proplist_copy(p);
|
|
|
|
|
pa_proplist_update(s->proplist, PA_UPDATE_MERGE, c->client->proplist);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-08-03 19:26:56 +00:00
|
|
|
pa_idxset_put(c->output_streams, s, &s->index);
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2004-08-03 19:26:56 +00:00
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-30 18:26:23 +02:00
|
|
|
/* Called from main context */
|
2007-10-28 19:13:50 +00:00
|
|
|
static void record_stream_unlink(record_stream *s) {
|
|
|
|
|
pa_assert(s);
|
|
|
|
|
|
|
|
|
|
if (!s->connection)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (s->source_output) {
|
|
|
|
|
pa_source_output_unlink(s->source_output);
|
|
|
|
|
pa_source_output_unref(s->source_output);
|
|
|
|
|
s->source_output = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pa_assert_se(pa_idxset_remove_by_data(s->connection->record_streams, s, NULL) == s);
|
|
|
|
|
s->connection = NULL;
|
|
|
|
|
record_stream_unref(s);
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-30 18:26:23 +02:00
|
|
|
/* Called from main context */
|
2007-10-28 19:13:50 +00:00
|
|
|
static void record_stream_free(pa_object *o) {
|
|
|
|
|
record_stream *s = RECORD_STREAM(o);
|
|
|
|
|
pa_assert(s);
|
|
|
|
|
|
|
|
|
|
record_stream_unlink(s);
|
|
|
|
|
|
|
|
|
|
pa_memblockq_free(s->memblockq);
|
|
|
|
|
pa_xfree(s);
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-30 18:26:23 +02:00
|
|
|
/* Called from main context */
|
2007-10-28 19:13:50 +00:00
|
|
|
static int record_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
|
|
|
|
|
record_stream *s = RECORD_STREAM(o);
|
|
|
|
|
record_stream_assert_ref(s);
|
2004-08-03 19:26:56 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
if (!s->connection)
|
|
|
|
|
return -1;
|
2004-08-03 19:26:56 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
switch (code) {
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
case RECORD_STREAM_MESSAGE_POST_DATA:
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2009-04-05 02:59:02 +02:00
|
|
|
/* We try to keep up to date with how many bytes are
|
|
|
|
|
* currently on the fly */
|
|
|
|
|
pa_atomic_sub(&s->on_the_fly, chunk->length);
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
if (pa_memblockq_push_align(s->memblockq, chunk) < 0) {
|
|
|
|
|
/* pa_log_warn("Failed to push data into output queue."); */
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!pa_pstream_is_pending(s->connection->pstream))
|
2008-08-04 18:58:29 +02:00
|
|
|
native_connection_send_memblock(s->connection);
|
2007-10-28 19:13:50 +00:00
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
2004-08-03 19:26:56 +00:00
|
|
|
}
|
|
|
|
|
|
2009-03-30 18:46:12 +02:00
|
|
|
/* Called from main context */
|
|
|
|
|
static void fix_record_buffer_attr_pre(record_stream *s) {
|
2008-09-03 18:31:46 +02:00
|
|
|
|
|
|
|
|
size_t frame_size;
|
|
|
|
|
pa_usec_t orig_fragsize_usec, fragsize_usec, source_usec;
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_assert(s);
|
2009-03-30 18:46:12 +02:00
|
|
|
|
|
|
|
|
/* This function will be called from the main thread, before as
|
|
|
|
|
* well as after the source output has been activated using
|
|
|
|
|
* pa_source_output_put()! That means it may not touch any
|
|
|
|
|
* ->thread_info data! */
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2008-09-03 18:31:46 +02:00
|
|
|
frame_size = pa_frame_size(&s->source_output->sample_spec);
|
2011-06-14 16:38:02 -07:00
|
|
|
s->buffer_attr = s->buffer_attr_req;
|
2008-09-03 18:31:46 +02:00
|
|
|
|
2009-03-30 18:46:12 +02:00
|
|
|
if (s->buffer_attr.maxlength == (uint32_t) -1 || s->buffer_attr.maxlength > MAX_MEMBLOCKQ_LENGTH)
|
|
|
|
|
s->buffer_attr.maxlength = MAX_MEMBLOCKQ_LENGTH;
|
|
|
|
|
if (s->buffer_attr.maxlength <= 0)
|
|
|
|
|
s->buffer_attr.maxlength = (uint32_t) frame_size;
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2009-03-30 18:46:12 +02:00
|
|
|
if (s->buffer_attr.fragsize == (uint32_t) -1)
|
|
|
|
|
s->buffer_attr.fragsize = (uint32_t) pa_usec_to_bytes(DEFAULT_FRAGSIZE_MSEC*PA_USEC_PER_MSEC, &s->source_output->sample_spec);
|
|
|
|
|
if (s->buffer_attr.fragsize <= 0)
|
|
|
|
|
s->buffer_attr.fragsize = (uint32_t) frame_size;
|
2008-09-03 18:31:46 +02:00
|
|
|
|
2009-03-30 18:46:12 +02:00
|
|
|
orig_fragsize_usec = fragsize_usec = pa_bytes_to_usec(s->buffer_attr.fragsize, &s->source_output->sample_spec);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2009-03-30 18:46:12 +02:00
|
|
|
if (s->early_requests) {
|
2008-09-03 18:31:46 +02:00
|
|
|
|
|
|
|
|
/* In early request mode we need to emulate the classic
|
2014-05-23 14:43:32 +02:00
|
|
|
* fragment-based playback model. Unfortunately we have no
|
|
|
|
|
* mechanism to tell the source how often we want it to send us
|
|
|
|
|
* data. The next best thing we can do is to set the source's
|
|
|
|
|
* total buffer (i.e. its latency) to the fragment size. That
|
|
|
|
|
* way it will have to send data at least that often. */
|
2008-09-03 18:31:46 +02:00
|
|
|
|
|
|
|
|
source_usec = fragsize_usec;
|
|
|
|
|
|
2009-03-30 18:46:12 +02:00
|
|
|
} else if (s->adjust_latency) {
|
2008-05-15 23:34:41 +00:00
|
|
|
|
|
|
|
|
/* So, the user asked us to adjust the latency according to
|
2015-04-13 14:53:45 +05:30
|
|
|
* what the source can provide. We set the source to whatever
|
|
|
|
|
* latency it can provide that is closest to what we want, and
|
|
|
|
|
* let the client buffer be equally large. This does NOT mean
|
|
|
|
|
* that we are doing (2 * fragsize) bytes of buffering, since
|
|
|
|
|
* the client-side buffer is only data that is on the way to
|
|
|
|
|
* the client. */
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2015-04-13 14:53:45 +05:30
|
|
|
source_usec = fragsize_usec;
|
2008-09-03 18:31:46 +02:00
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
|
|
/* Ok, the user didn't ask us to adjust the latency, hence we
|
|
|
|
|
* don't */
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2009-04-05 02:59:02 +02:00
|
|
|
source_usec = (pa_usec_t) -1;
|
2008-09-03 18:31:46 +02:00
|
|
|
}
|
|
|
|
|
|
2009-04-05 02:59:02 +02:00
|
|
|
if (source_usec != (pa_usec_t) -1)
|
|
|
|
|
s->configured_source_latency = pa_source_output_set_requested_latency(s->source_output, source_usec);
|
2008-09-03 18:31:46 +02:00
|
|
|
else
|
2009-04-05 02:59:02 +02:00
|
|
|
s->configured_source_latency = 0;
|
2008-09-03 18:31:46 +02:00
|
|
|
|
2009-03-30 18:46:12 +02:00
|
|
|
if (s->early_requests) {
|
2008-09-03 18:31:46 +02:00
|
|
|
|
2014-05-23 14:43:32 +02:00
|
|
|
/* Ok, we didn't necessarily get what we were asking for. We
|
|
|
|
|
* might still get the proper fragment interval, we just can't
|
|
|
|
|
* guarantee it. */
|
2008-09-03 18:31:46 +02:00
|
|
|
|
2014-05-23 14:43:32 +02:00
|
|
|
if (fragsize_usec != s->configured_source_latency)
|
2018-09-19 11:29:45 -07:00
|
|
|
pa_log_debug("Could not configure a sufficiently low latency. Early requests might not be satisfied.");
|
2008-09-03 18:31:46 +02:00
|
|
|
|
2009-03-30 18:46:12 +02:00
|
|
|
} else if (s->adjust_latency) {
|
2008-09-03 18:31:46 +02:00
|
|
|
|
2015-04-13 14:53:45 +05:30
|
|
|
/* We keep the client buffer large enough to transfer one
|
|
|
|
|
* hardware-buffer-sized chunk at a time to the client. */
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2015-04-13 14:53:45 +05:30
|
|
|
fragsize_usec = s->configured_source_latency;
|
2008-09-03 18:31:46 +02:00
|
|
|
}
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2008-09-03 18:31:46 +02:00
|
|
|
if (pa_usec_to_bytes(orig_fragsize_usec, &s->source_output->sample_spec) !=
|
|
|
|
|
pa_usec_to_bytes(fragsize_usec, &s->source_output->sample_spec))
|
2008-09-01 02:41:03 +02:00
|
|
|
|
2009-03-30 18:46:12 +02:00
|
|
|
s->buffer_attr.fragsize = (uint32_t) pa_usec_to_bytes(fragsize_usec, &s->source_output->sample_spec);
|
2008-09-03 18:31:46 +02:00
|
|
|
|
2009-03-30 18:46:12 +02:00
|
|
|
if (s->buffer_attr.fragsize <= 0)
|
|
|
|
|
s->buffer_attr.fragsize = (uint32_t) frame_size;
|
2008-05-15 23:34:41 +00:00
|
|
|
}
|
|
|
|
|
|
2009-03-30 18:46:12 +02:00
|
|
|
/* Called from main context */
|
|
|
|
|
static void fix_record_buffer_attr_post(record_stream *s) {
|
2008-05-15 23:34:41 +00:00
|
|
|
size_t base;
|
|
|
|
|
|
|
|
|
|
pa_assert(s);
|
|
|
|
|
|
2009-03-30 18:46:12 +02:00
|
|
|
/* This function will be called from the main thread, before as
|
|
|
|
|
* well as after the source output has been activated using
|
|
|
|
|
* pa_source_output_put()! That means it may not touch and
|
|
|
|
|
* ->thread_info data! */
|
2008-05-15 23:34:41 +00:00
|
|
|
|
|
|
|
|
base = pa_frame_size(&s->source_output->sample_spec);
|
|
|
|
|
|
2009-03-30 18:46:12 +02:00
|
|
|
s->buffer_attr.fragsize = (s->buffer_attr.fragsize/base)*base;
|
|
|
|
|
if (s->buffer_attr.fragsize <= 0)
|
|
|
|
|
s->buffer_attr.fragsize = base;
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2009-03-30 18:46:12 +02:00
|
|
|
if (s->buffer_attr.fragsize > s->buffer_attr.maxlength)
|
|
|
|
|
s->buffer_attr.fragsize = s->buffer_attr.maxlength;
|
2008-05-15 23:34:41 +00:00
|
|
|
}
|
|
|
|
|
|
2009-03-30 18:26:23 +02:00
|
|
|
/* Called from main context */
|
2007-10-28 19:13:50 +00:00
|
|
|
static record_stream* record_stream_new(
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c,
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_source *source,
|
2007-11-21 01:30:40 +00:00
|
|
|
pa_sample_spec *ss,
|
|
|
|
|
pa_channel_map *map,
|
2011-05-17 21:56:10 +01:00
|
|
|
pa_idxset *formats,
|
2009-03-30 18:46:12 +02:00
|
|
|
pa_buffer_attr *attr,
|
2011-05-17 22:31:10 +01:00
|
|
|
pa_cvolume *volume,
|
2013-06-27 19:28:09 +02:00
|
|
|
bool muted,
|
|
|
|
|
bool muted_set,
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_source_output_flags_t flags,
|
|
|
|
|
pa_proplist *p,
|
2013-06-27 19:28:09 +02:00
|
|
|
bool adjust_latency,
|
|
|
|
|
bool early_requests,
|
|
|
|
|
bool relative_volume,
|
|
|
|
|
bool peak_detect,
|
2011-05-17 21:56:10 +01:00
|
|
|
pa_sink_input *direct_on_input,
|
2009-02-03 03:14:20 +01:00
|
|
|
int *ret) {
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2018-05-28 21:14:00 +05:30
|
|
|
/* Note: This function takes ownership of the 'formats' param, so we need
|
|
|
|
|
* to take extra care to not leak it */
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
record_stream *s;
|
2009-02-04 18:33:01 +01:00
|
|
|
pa_source_output *source_output = NULL;
|
2006-08-13 19:55:17 +00:00
|
|
|
pa_source_output_new_data data;
|
2011-09-29 18:54:03 +03:00
|
|
|
char *memblockq_name;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(ss);
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_assert(p);
|
2009-02-03 03:14:20 +01:00
|
|
|
pa_assert(ret);
|
2004-07-10 16:50:22 +00:00
|
|
|
|
2006-08-13 19:55:17 +00:00
|
|
|
pa_source_output_new_data_init(&data);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
|
|
|
|
pa_proplist_update(data.proplist, PA_UPDATE_REPLACE, p);
|
|
|
|
|
data.driver = __FILE__;
|
2008-08-03 16:44:38 +02:00
|
|
|
data.module = c->options->module;
|
2007-10-28 19:13:50 +00:00
|
|
|
data.client = c->client;
|
2011-05-17 21:56:10 +01:00
|
|
|
if (source)
|
2017-11-28 19:50:01 +02:00
|
|
|
pa_source_output_new_data_set_source(&data, source, false, true);
|
2011-05-17 21:56:10 +01:00
|
|
|
if (pa_sample_spec_valid(ss))
|
|
|
|
|
pa_source_output_new_data_set_sample_spec(&data, ss);
|
|
|
|
|
if (pa_channel_map_valid(map))
|
|
|
|
|
pa_source_output_new_data_set_channel_map(&data, map);
|
|
|
|
|
if (formats)
|
|
|
|
|
pa_source_output_new_data_set_formats(&data, formats);
|
2008-06-13 21:56:19 +00:00
|
|
|
data.direct_on_input = direct_on_input;
|
2011-05-17 22:31:10 +01:00
|
|
|
if (volume) {
|
|
|
|
|
pa_source_output_new_data_set_volume(&data, volume);
|
|
|
|
|
data.volume_is_absolute = !relative_volume;
|
2013-06-27 19:28:09 +02:00
|
|
|
data.save_volume = false;
|
2011-05-17 22:31:10 +01:00
|
|
|
}
|
|
|
|
|
if (muted_set) {
|
|
|
|
|
pa_source_output_new_data_set_muted(&data, muted);
|
2013-06-27 19:28:09 +02:00
|
|
|
data.save_muted = false;
|
2011-05-17 22:31:10 +01:00
|
|
|
}
|
2008-05-15 23:34:41 +00:00
|
|
|
if (peak_detect)
|
|
|
|
|
data.resample_method = PA_RESAMPLER_PEAKS;
|
2009-08-28 23:24:09 +02:00
|
|
|
data.flags = flags;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2009-08-28 23:24:09 +02:00
|
|
|
*ret = -pa_source_output_new(&source_output, c->protocol->core, &data);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
|
|
|
|
pa_source_output_new_data_done(&data);
|
|
|
|
|
|
|
|
|
|
if (!source_output)
|
2004-07-10 16:50:22 +00:00
|
|
|
return NULL;
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
s = pa_msgobject_new(record_stream);
|
|
|
|
|
s->parent.parent.free = record_stream_free;
|
|
|
|
|
s->parent.process_msg = record_stream_process_msg;
|
2004-07-10 16:50:22 +00:00
|
|
|
s->connection = c;
|
|
|
|
|
s->source_output = source_output;
|
2011-06-14 16:38:02 -07:00
|
|
|
s->buffer_attr_req = *attr;
|
2009-03-30 18:46:12 +02:00
|
|
|
s->adjust_latency = adjust_latency;
|
|
|
|
|
s->early_requests = early_requests;
|
2009-04-05 02:59:02 +02:00
|
|
|
pa_atomic_store(&s->on_the_fly, 0);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2009-04-05 02:59:02 +02:00
|
|
|
s->source_output->parent.process_msg = source_output_process_msg;
|
2004-07-10 16:50:22 +00:00
|
|
|
s->source_output->push = source_output_push_cb;
|
|
|
|
|
s->source_output->kill = source_output_kill_cb;
|
2004-09-16 00:05:56 +00:00
|
|
|
s->source_output->get_latency = source_output_get_latency_cb;
|
2009-03-30 18:21:34 +02:00
|
|
|
s->source_output->moving = source_output_moving_cb;
|
2007-11-21 01:30:40 +00:00
|
|
|
s->source_output->suspend = source_output_suspend_cb;
|
2009-02-12 03:18:05 +01:00
|
|
|
s->source_output->send_event = source_output_send_event_cb;
|
2004-07-10 16:50:22 +00:00
|
|
|
s->source_output->userdata = s;
|
|
|
|
|
|
2009-03-30 18:46:12 +02:00
|
|
|
fix_record_buffer_attr_pre(s);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2011-09-29 18:54:03 +03:00
|
|
|
memblockq_name = pa_sprintf_malloc("native protocol record stream memblockq [%u]", s->source_output->index);
|
2006-02-20 04:05:16 +00:00
|
|
|
s->memblockq = pa_memblockq_new(
|
2011-09-29 18:54:03 +03:00
|
|
|
memblockq_name,
|
2006-02-20 04:05:16 +00:00
|
|
|
0,
|
2009-03-30 18:46:12 +02:00
|
|
|
s->buffer_attr.maxlength,
|
2006-02-20 04:05:16 +00:00
|
|
|
0,
|
2011-09-29 18:54:03 +03:00
|
|
|
&source_output->sample_spec,
|
2006-02-20 04:05:16 +00:00
|
|
|
1,
|
|
|
|
|
0,
|
2008-05-15 23:34:41 +00:00
|
|
|
0,
|
2006-08-18 19:55:18 +00:00
|
|
|
NULL);
|
2011-09-29 18:54:03 +03:00
|
|
|
pa_xfree(memblockq_name);
|
2004-07-10 16:50:22 +00:00
|
|
|
|
2009-03-30 18:46:12 +02:00
|
|
|
pa_memblockq_get_attr(s->memblockq, &s->buffer_attr);
|
|
|
|
|
fix_record_buffer_attr_post(s);
|
2007-11-21 01:30:40 +00:00
|
|
|
|
|
|
|
|
*ss = s->source_output->sample_spec;
|
|
|
|
|
*map = s->source_output->channel_map;
|
2004-07-10 16:50:22 +00:00
|
|
|
|
|
|
|
|
pa_idxset_put(c->record_streams, s, &s->index);
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2008-05-17 09:11:21 +00:00
|
|
|
pa_log_info("Final latency %0.2f ms = %0.2f ms + %0.2f ms",
|
2009-04-05 02:59:02 +02:00
|
|
|
((double) pa_bytes_to_usec(s->buffer_attr.fragsize, &source_output->sample_spec) + (double) s->configured_source_latency) / PA_USEC_PER_MSEC,
|
2009-03-30 18:46:12 +02:00
|
|
|
(double) pa_bytes_to_usec(s->buffer_attr.fragsize, &source_output->sample_spec) / PA_USEC_PER_MSEC,
|
2009-04-05 02:59:02 +02:00
|
|
|
(double) s->configured_source_latency / PA_USEC_PER_MSEC);
|
2008-05-17 09:11:21 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_source_output_put(s->source_output);
|
2004-07-10 16:50:22 +00:00
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-30 18:26:23 +02:00
|
|
|
/* Called from main context */
|
2008-08-04 18:58:29 +02:00
|
|
|
static void record_stream_send_killed(record_stream *r) {
|
|
|
|
|
pa_tagstruct *t;
|
|
|
|
|
record_stream_assert_ref(r);
|
|
|
|
|
|
2014-10-23 16:09:45 +02:00
|
|
|
t = pa_tagstruct_new();
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_KILLED);
|
|
|
|
|
pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
|
|
|
|
|
pa_tagstruct_putu32(t, r->index);
|
|
|
|
|
pa_pstream_send_tagstruct(r->connection->pstream, t);
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-30 18:26:23 +02:00
|
|
|
/* Called from main context */
|
2007-10-28 19:13:50 +00:00
|
|
|
static void playback_stream_unlink(playback_stream *s) {
|
|
|
|
|
pa_assert(s);
|
|
|
|
|
|
|
|
|
|
if (!s->connection)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (s->sink_input) {
|
|
|
|
|
pa_sink_input_unlink(s->sink_input);
|
|
|
|
|
pa_sink_input_unref(s->sink_input);
|
|
|
|
|
s->sink_input = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (s->drain_request)
|
|
|
|
|
pa_pstream_send_error(s->connection->pstream, s->drain_tag, PA_ERR_NOENTITY);
|
|
|
|
|
|
|
|
|
|
pa_assert_se(pa_idxset_remove_by_data(s->connection->output_streams, s, NULL) == s);
|
|
|
|
|
s->connection = NULL;
|
|
|
|
|
playback_stream_unref(s);
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-30 18:26:23 +02:00
|
|
|
/* Called from main context */
|
2007-10-28 19:13:50 +00:00
|
|
|
static void playback_stream_free(pa_object* o) {
|
|
|
|
|
playback_stream *s = PLAYBACK_STREAM(o);
|
|
|
|
|
pa_assert(s);
|
|
|
|
|
|
|
|
|
|
playback_stream_unlink(s);
|
|
|
|
|
|
|
|
|
|
pa_memblockq_free(s->memblockq);
|
|
|
|
|
pa_xfree(s);
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-30 18:26:23 +02:00
|
|
|
/* Called from main context */
|
2007-10-28 19:13:50 +00:00
|
|
|
static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
|
|
|
|
|
playback_stream *s = PLAYBACK_STREAM(o);
|
|
|
|
|
playback_stream_assert_ref(s);
|
|
|
|
|
|
|
|
|
|
if (!s->connection)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
switch (code) {
|
2009-08-15 00:05:17 +02:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
case PLAYBACK_STREAM_MESSAGE_REQUEST_DATA: {
|
|
|
|
|
pa_tagstruct *t;
|
2009-04-01 23:05:09 +02:00
|
|
|
int l = 0;
|
2007-10-28 19:13:50 +00:00
|
|
|
|
|
|
|
|
for (;;) {
|
2009-04-01 23:05:09 +02:00
|
|
|
if ((l = pa_atomic_load(&s->missing)) <= 0)
|
|
|
|
|
return 0;
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2009-04-01 23:05:09 +02:00
|
|
|
if (pa_atomic_cmpxchg(&s->missing, l, 0))
|
2007-10-28 19:13:50 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-23 16:09:45 +02:00
|
|
|
t = pa_tagstruct_new();
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_tagstruct_putu32(t, PA_COMMAND_REQUEST);
|
|
|
|
|
pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
|
|
|
|
|
pa_tagstruct_putu32(t, s->index);
|
2009-04-01 23:05:09 +02:00
|
|
|
pa_tagstruct_putu32(t, (uint32_t) l);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_pstream_send_tagstruct(s->connection->pstream, t);
|
|
|
|
|
|
2011-11-10 12:43:09 +05:30
|
|
|
#ifdef PROTOCOL_NATIVE_DEBUG
|
|
|
|
|
pa_log("Requesting %lu bytes", (unsigned long) l);
|
|
|
|
|
#endif
|
2007-10-28 19:13:50 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case PLAYBACK_STREAM_MESSAGE_UNDERFLOW: {
|
|
|
|
|
pa_tagstruct *t;
|
|
|
|
|
|
2011-11-10 12:43:09 +05:30
|
|
|
#ifdef PROTOCOL_NATIVE_DEBUG
|
|
|
|
|
pa_log("signalling underflow");
|
|
|
|
|
#endif
|
2008-09-03 18:31:46 +02:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
/* Report that we're empty */
|
2014-10-23 16:09:45 +02:00
|
|
|
t = pa_tagstruct_new();
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_tagstruct_putu32(t, PA_COMMAND_UNDERFLOW);
|
|
|
|
|
pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
|
|
|
|
|
pa_tagstruct_putu32(t, s->index);
|
2011-08-02 14:34:20 +02:00
|
|
|
if (s->connection->version >= 23)
|
|
|
|
|
pa_tagstruct_puts64(t, offset);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_pstream_send_tagstruct(s->connection->pstream, t);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case PLAYBACK_STREAM_MESSAGE_OVERFLOW: {
|
|
|
|
|
pa_tagstruct *t;
|
|
|
|
|
|
|
|
|
|
/* Notify the user we're overflowed*/
|
2014-10-23 16:09:45 +02:00
|
|
|
t = pa_tagstruct_new();
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_tagstruct_putu32(t, PA_COMMAND_OVERFLOW);
|
|
|
|
|
pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
|
|
|
|
|
pa_tagstruct_putu32(t, s->index);
|
|
|
|
|
pa_pstream_send_tagstruct(s->connection->pstream, t);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2004-06-20 01:12:13 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
case PLAYBACK_STREAM_MESSAGE_STARTED:
|
|
|
|
|
|
|
|
|
|
if (s->connection->version >= 13) {
|
|
|
|
|
pa_tagstruct *t;
|
|
|
|
|
|
2009-03-19 19:38:30 +01:00
|
|
|
/* Notify the user we started playback */
|
2014-10-23 16:09:45 +02:00
|
|
|
t = pa_tagstruct_new();
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_tagstruct_putu32(t, PA_COMMAND_STARTED);
|
|
|
|
|
pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
|
|
|
|
|
pa_tagstruct_putu32(t, s->index);
|
|
|
|
|
pa_pstream_send_tagstruct(s->connection->pstream, t);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
case PLAYBACK_STREAM_MESSAGE_DRAIN_ACK:
|
|
|
|
|
pa_pstream_send_simple_ack(s->connection->pstream, PA_PTR_TO_UINT(userdata));
|
|
|
|
|
break;
|
2009-03-30 18:46:12 +02:00
|
|
|
|
2009-09-10 02:15:12 +02:00
|
|
|
case PLAYBACK_STREAM_MESSAGE_UPDATE_TLENGTH:
|
2009-03-30 18:46:12 +02:00
|
|
|
|
|
|
|
|
s->buffer_attr.tlength = (uint32_t) offset;
|
|
|
|
|
|
2009-09-10 02:15:12 +02:00
|
|
|
if (s->connection->version >= 15) {
|
|
|
|
|
pa_tagstruct *t;
|
|
|
|
|
|
2014-10-23 16:09:45 +02:00
|
|
|
t = pa_tagstruct_new();
|
2009-09-10 02:15:12 +02:00
|
|
|
pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED);
|
|
|
|
|
pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
|
|
|
|
|
pa_tagstruct_putu32(t, s->index);
|
|
|
|
|
pa_tagstruct_putu32(t, s->buffer_attr.maxlength);
|
|
|
|
|
pa_tagstruct_putu32(t, s->buffer_attr.tlength);
|
|
|
|
|
pa_tagstruct_putu32(t, s->buffer_attr.prebuf);
|
|
|
|
|
pa_tagstruct_putu32(t, s->buffer_attr.minreq);
|
|
|
|
|
pa_tagstruct_put_usec(t, s->configured_sink_latency);
|
|
|
|
|
pa_pstream_send_tagstruct(s->connection->pstream, t);
|
|
|
|
|
}
|
2009-03-30 18:46:12 +02:00
|
|
|
|
|
|
|
|
break;
|
2007-10-28 19:13:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
2004-06-20 01:12:13 +00:00
|
|
|
}
|
|
|
|
|
|
2009-03-30 18:46:12 +02:00
|
|
|
/* Called from main context */
|
|
|
|
|
static void fix_playback_buffer_attr(playback_stream *s) {
|
2009-04-06 22:09:38 +02:00
|
|
|
size_t frame_size, max_prebuf;
|
2008-09-01 02:41:03 +02:00
|
|
|
pa_usec_t orig_tlength_usec, tlength_usec, orig_minreq_usec, minreq_usec, sink_usec;
|
2008-05-15 23:34:41 +00:00
|
|
|
|
|
|
|
|
pa_assert(s);
|
2009-03-30 18:46:12 +02:00
|
|
|
|
2011-11-10 12:43:09 +05:30
|
|
|
#ifdef PROTOCOL_NATIVE_DEBUG
|
|
|
|
|
pa_log("Client requested: maxlength=%li bytes tlength=%li bytes minreq=%li bytes prebuf=%li bytes",
|
2012-11-26 12:12:05 +05:30
|
|
|
(long) s->buffer_attr_req.maxlength,
|
|
|
|
|
(long) s->buffer_attr_req.tlength,
|
|
|
|
|
(long) s->buffer_attr_req.minreq,
|
|
|
|
|
(long) s->buffer_attr_req.prebuf);
|
2011-11-10 12:43:09 +05:30
|
|
|
|
|
|
|
|
pa_log("Client requested: maxlength=%lu ms tlength=%lu ms minreq=%lu ms prebuf=%lu ms",
|
2012-11-26 12:12:05 +05:30
|
|
|
(unsigned long) (pa_bytes_to_usec(s->buffer_attr_req.maxlength, &s->sink_input->sample_spec) / PA_USEC_PER_MSEC),
|
|
|
|
|
(unsigned long) (pa_bytes_to_usec(s->buffer_attr_req.tlength, &s->sink_input->sample_spec) / PA_USEC_PER_MSEC),
|
|
|
|
|
(unsigned long) (pa_bytes_to_usec(s->buffer_attr_req.minreq, &s->sink_input->sample_spec) / PA_USEC_PER_MSEC),
|
|
|
|
|
(unsigned long) (pa_bytes_to_usec(s->buffer_attr_req.prebuf, &s->sink_input->sample_spec) / PA_USEC_PER_MSEC));
|
2011-11-10 12:43:09 +05:30
|
|
|
#endif
|
2010-02-22 03:40:29 +01:00
|
|
|
|
2009-03-30 18:46:12 +02:00
|
|
|
/* This function will be called from the main thread, before as
|
|
|
|
|
* well as after the sink input has been activated using
|
|
|
|
|
* pa_sink_input_put()! That means it may not touch any
|
|
|
|
|
* ->thread_info data, such as the memblockq! */
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2008-06-27 00:34:17 +02:00
|
|
|
frame_size = pa_frame_size(&s->sink_input->sample_spec);
|
2011-06-14 16:38:02 -07:00
|
|
|
s->buffer_attr = s->buffer_attr_req;
|
2008-06-27 00:34:17 +02:00
|
|
|
|
2009-03-30 18:46:12 +02:00
|
|
|
if (s->buffer_attr.maxlength == (uint32_t) -1 || s->buffer_attr.maxlength > MAX_MEMBLOCKQ_LENGTH)
|
|
|
|
|
s->buffer_attr.maxlength = MAX_MEMBLOCKQ_LENGTH;
|
|
|
|
|
if (s->buffer_attr.maxlength <= 0)
|
|
|
|
|
s->buffer_attr.maxlength = (uint32_t) frame_size;
|
2008-06-27 00:34:17 +02:00
|
|
|
|
2009-03-30 18:46:12 +02:00
|
|
|
if (s->buffer_attr.tlength == (uint32_t) -1)
|
|
|
|
|
s->buffer_attr.tlength = (uint32_t) pa_usec_to_bytes_round_up(DEFAULT_TLENGTH_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec);
|
|
|
|
|
if (s->buffer_attr.tlength <= 0)
|
|
|
|
|
s->buffer_attr.tlength = (uint32_t) frame_size;
|
2013-03-25 09:20:14 +01:00
|
|
|
if (s->buffer_attr.tlength > s->buffer_attr.maxlength)
|
|
|
|
|
s->buffer_attr.tlength = s->buffer_attr.maxlength;
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2013-03-25 09:20:15 +01:00
|
|
|
if (s->buffer_attr.minreq == (uint32_t) -1) {
|
|
|
|
|
uint32_t process = (uint32_t) pa_usec_to_bytes_round_up(DEFAULT_PROCESS_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec);
|
|
|
|
|
/* With low-latency, tlength/4 gives a decent default in all of traditional, adjust latency and early request modes. */
|
|
|
|
|
uint32_t m = s->buffer_attr.tlength / 4;
|
|
|
|
|
if (frame_size)
|
|
|
|
|
m -= m % frame_size;
|
|
|
|
|
s->buffer_attr.minreq = PA_MIN(process, m);
|
|
|
|
|
}
|
2009-03-30 18:46:12 +02:00
|
|
|
if (s->buffer_attr.minreq <= 0)
|
|
|
|
|
s->buffer_attr.minreq = (uint32_t) frame_size;
|
2008-06-27 00:34:17 +02:00
|
|
|
|
2009-03-30 18:46:12 +02:00
|
|
|
if (s->buffer_attr.tlength < s->buffer_attr.minreq+frame_size)
|
|
|
|
|
s->buffer_attr.tlength = s->buffer_attr.minreq+(uint32_t) frame_size;
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2009-03-30 18:46:12 +02:00
|
|
|
orig_tlength_usec = tlength_usec = pa_bytes_to_usec(s->buffer_attr.tlength, &s->sink_input->sample_spec);
|
|
|
|
|
orig_minreq_usec = minreq_usec = pa_bytes_to_usec(s->buffer_attr.minreq, &s->sink_input->sample_spec);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
|
|
|
|
pa_log_info("Requested tlength=%0.2f ms, minreq=%0.2f ms",
|
|
|
|
|
(double) tlength_usec / PA_USEC_PER_MSEC,
|
|
|
|
|
(double) minreq_usec / PA_USEC_PER_MSEC);
|
|
|
|
|
|
2009-03-30 18:46:12 +02:00
|
|
|
if (s->early_requests) {
|
2008-09-03 18:31:46 +02:00
|
|
|
|
|
|
|
|
/* In early request mode we need to emulate the classic
|
2014-05-23 14:43:32 +02:00
|
|
|
* fragment-based playback model. Unfortunately we have no
|
|
|
|
|
* mechanism to tell the sink how often we want to be queried
|
|
|
|
|
* for data. The next best thing we can do is to set the sink's
|
|
|
|
|
* total buffer (i.e. its latency) to the fragment size. That
|
|
|
|
|
* way it will have to query us at least that often. */
|
2008-09-03 18:31:46 +02:00
|
|
|
|
|
|
|
|
sink_usec = minreq_usec;
|
|
|
|
|
pa_log_debug("Early requests mode enabled, configuring sink latency to minreq.");
|
|
|
|
|
|
2009-03-30 18:46:12 +02:00
|
|
|
} else if (s->adjust_latency) {
|
2008-05-15 23:34:41 +00:00
|
|
|
|
|
|
|
|
/* So, the user asked us to adjust the latency of the stream
|
|
|
|
|
* buffer according to the what the sink can provide. The
|
|
|
|
|
* tlength passed in shall be the overall latency. Roughly
|
|
|
|
|
* half the latency will be spent on the hw buffer, the other
|
|
|
|
|
* half of it in the async buffer queue we maintain for each
|
|
|
|
|
* client. In between we'll have a safety space of size
|
2011-08-24 18:24:46 +02:00
|
|
|
* 2*minreq. Why the 2*minreq? When the hw buffer is completely
|
2008-05-15 23:34:41 +00:00
|
|
|
* empty and needs to be filled, then our buffer must have
|
2011-08-24 18:24:46 +02:00
|
|
|
* enough data to fulfill this request immediately and thus
|
2008-05-15 23:34:41 +00:00
|
|
|
* have at least the same tlength as the size of the hw
|
|
|
|
|
* buffer. It additionally needs space for 2 times minreq
|
|
|
|
|
* because if the buffer ran empty and a partial fillup
|
|
|
|
|
* happens immediately on the next iteration we need to be
|
|
|
|
|
* able to fulfill it and give the application also minreq
|
|
|
|
|
* time to fill it up again for the next request Makes 2 times
|
|
|
|
|
* minreq in plus.. */
|
|
|
|
|
|
|
|
|
|
if (tlength_usec > minreq_usec*2)
|
|
|
|
|
sink_usec = (tlength_usec - minreq_usec*2)/2;
|
|
|
|
|
else
|
|
|
|
|
sink_usec = 0;
|
|
|
|
|
|
2008-09-03 18:31:46 +02:00
|
|
|
pa_log_debug("Adjust latency mode enabled, configuring sink latency to half of overall latency.");
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
} else {
|
|
|
|
|
|
|
|
|
|
/* Ok, the user didn't ask us to adjust the latency, but we
|
|
|
|
|
* still need to make sure that the parameters from the user
|
|
|
|
|
* do make sense. */
|
|
|
|
|
|
|
|
|
|
if (tlength_usec > minreq_usec*2)
|
|
|
|
|
sink_usec = (tlength_usec - minreq_usec*2);
|
|
|
|
|
else
|
|
|
|
|
sink_usec = 0;
|
2008-09-03 18:31:46 +02:00
|
|
|
|
|
|
|
|
pa_log_debug("Traditional mode enabled, modifying sink usec only for compat with minreq.");
|
2008-05-15 23:34:41 +00:00
|
|
|
}
|
|
|
|
|
|
2009-04-05 02:59:02 +02:00
|
|
|
s->configured_sink_latency = pa_sink_input_set_requested_latency(s->sink_input, sink_usec);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2009-03-30 18:46:12 +02:00
|
|
|
if (s->early_requests) {
|
2008-09-03 18:31:46 +02:00
|
|
|
|
2014-05-23 14:43:32 +02:00
|
|
|
/* Ok, we didn't necessarily get what we were asking for. We
|
|
|
|
|
* might still get the proper fragment interval, we just can't
|
|
|
|
|
* guarantee it. */
|
2008-09-03 18:31:46 +02:00
|
|
|
|
2014-05-23 14:43:32 +02:00
|
|
|
if (minreq_usec != s->configured_sink_latency)
|
2018-09-19 11:29:45 -07:00
|
|
|
pa_log_debug("Could not configure a sufficiently low latency. Early requests might not be satisfied.");
|
2008-09-03 18:31:46 +02:00
|
|
|
|
2009-03-30 18:46:12 +02:00
|
|
|
} else if (s->adjust_latency) {
|
2008-09-03 18:31:46 +02:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
/* Ok, we didn't necessarily get what we were asking for, so
|
|
|
|
|
* let's subtract from what we asked for for the remaining
|
|
|
|
|
* buffer space */
|
|
|
|
|
|
2009-04-05 02:59:02 +02:00
|
|
|
if (tlength_usec >= s->configured_sink_latency)
|
|
|
|
|
tlength_usec -= s->configured_sink_latency;
|
2008-05-15 23:34:41 +00:00
|
|
|
}
|
|
|
|
|
|
2011-11-03 21:14:45 +01:00
|
|
|
pa_log_debug("Requested latency=%0.2f ms, Received latency=%0.2f ms",
|
|
|
|
|
(double) sink_usec / PA_USEC_PER_MSEC,
|
|
|
|
|
(double) s->configured_sink_latency / PA_USEC_PER_MSEC);
|
|
|
|
|
|
2008-06-28 01:09:07 +02:00
|
|
|
/* FIXME: This is actually larger than necessary, since not all of
|
|
|
|
|
* the sink latency is actually rewritable. */
|
2009-04-05 02:59:02 +02:00
|
|
|
if (tlength_usec < s->configured_sink_latency + 2*minreq_usec)
|
|
|
|
|
tlength_usec = s->configured_sink_latency + 2*minreq_usec;
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2008-09-01 02:41:03 +02:00
|
|
|
if (pa_usec_to_bytes_round_up(orig_tlength_usec, &s->sink_input->sample_spec) !=
|
|
|
|
|
pa_usec_to_bytes_round_up(tlength_usec, &s->sink_input->sample_spec))
|
2009-03-30 18:46:12 +02:00
|
|
|
s->buffer_attr.tlength = (uint32_t) pa_usec_to_bytes_round_up(tlength_usec, &s->sink_input->sample_spec);
|
2008-09-01 02:41:03 +02:00
|
|
|
|
|
|
|
|
if (pa_usec_to_bytes(orig_minreq_usec, &s->sink_input->sample_spec) !=
|
|
|
|
|
pa_usec_to_bytes(minreq_usec, &s->sink_input->sample_spec))
|
2009-03-30 18:46:12 +02:00
|
|
|
s->buffer_attr.minreq = (uint32_t) pa_usec_to_bytes(minreq_usec, &s->sink_input->sample_spec);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2009-03-30 18:46:12 +02:00
|
|
|
if (s->buffer_attr.minreq <= 0) {
|
|
|
|
|
s->buffer_attr.minreq = (uint32_t) frame_size;
|
|
|
|
|
s->buffer_attr.tlength += (uint32_t) frame_size*2;
|
2008-05-15 23:34:41 +00:00
|
|
|
}
|
|
|
|
|
|
2009-03-30 18:46:12 +02:00
|
|
|
if (s->buffer_attr.tlength <= s->buffer_attr.minreq)
|
|
|
|
|
s->buffer_attr.tlength = s->buffer_attr.minreq*2 + (uint32_t) frame_size;
|
2008-09-03 18:31:46 +02:00
|
|
|
|
2009-04-06 22:09:38 +02:00
|
|
|
max_prebuf = s->buffer_attr.tlength + (uint32_t)frame_size - s->buffer_attr.minreq;
|
|
|
|
|
|
|
|
|
|
if (s->buffer_attr.prebuf == (uint32_t) -1 ||
|
|
|
|
|
s->buffer_attr.prebuf > max_prebuf)
|
|
|
|
|
s->buffer_attr.prebuf = max_prebuf;
|
2010-02-22 03:40:29 +01:00
|
|
|
|
2011-11-10 12:43:09 +05:30
|
|
|
#ifdef PROTOCOL_NATIVE_DEBUG
|
|
|
|
|
pa_log("Client accepted: maxlength=%lu ms tlength=%lu ms minreq=%lu ms prebuf=%lu ms",
|
|
|
|
|
(unsigned long) (pa_bytes_to_usec(s->buffer_attr.maxlength, &s->sink_input->sample_spec) / PA_USEC_PER_MSEC),
|
|
|
|
|
(unsigned long) (pa_bytes_to_usec(s->buffer_attr.tlength, &s->sink_input->sample_spec) / PA_USEC_PER_MSEC),
|
|
|
|
|
(unsigned long) (pa_bytes_to_usec(s->buffer_attr.minreq, &s->sink_input->sample_spec) / PA_USEC_PER_MSEC),
|
|
|
|
|
(unsigned long) (pa_bytes_to_usec(s->buffer_attr.prebuf, &s->sink_input->sample_spec) / PA_USEC_PER_MSEC));
|
|
|
|
|
#endif
|
2008-05-15 23:34:41 +00:00
|
|
|
}
|
|
|
|
|
|
2009-03-30 18:26:23 +02:00
|
|
|
/* Called from main context */
|
2007-10-28 19:13:50 +00:00
|
|
|
static playback_stream* playback_stream_new(
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c,
|
2006-08-13 16:19:56 +00:00
|
|
|
pa_sink *sink,
|
2007-11-21 01:30:40 +00:00
|
|
|
pa_sample_spec *ss,
|
|
|
|
|
pa_channel_map *map,
|
2011-02-28 13:23:23 +05:30
|
|
|
pa_idxset *formats,
|
2009-03-30 18:46:12 +02:00
|
|
|
pa_buffer_attr *a,
|
2006-08-13 16:19:56 +00:00
|
|
|
pa_cvolume *volume,
|
2013-06-27 19:28:09 +02:00
|
|
|
bool muted,
|
|
|
|
|
bool muted_set,
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_sink_input_flags_t flags,
|
|
|
|
|
pa_proplist *p,
|
2013-06-27 19:28:09 +02:00
|
|
|
bool adjust_latency,
|
|
|
|
|
bool early_requests,
|
|
|
|
|
bool relative_volume,
|
2011-05-17 21:56:10 +01:00
|
|
|
uint32_t syncid,
|
|
|
|
|
uint32_t *missing,
|
2009-02-03 03:14:20 +01:00
|
|
|
int *ret) {
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2011-06-02 19:35:09 +05:30
|
|
|
/* Note: This function takes ownership of the 'formats' param, so we need
|
|
|
|
|
* to take extra care to not leak it */
|
|
|
|
|
|
2011-08-13 13:43:20 +02:00
|
|
|
playback_stream *ssync;
|
|
|
|
|
playback_stream *s = NULL;
|
2009-02-04 18:33:01 +01:00
|
|
|
pa_sink_input *sink_input = NULL;
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_memchunk silence;
|
2006-02-20 04:05:16 +00:00
|
|
|
uint32_t idx;
|
|
|
|
|
int64_t start_index;
|
2006-08-13 16:19:56 +00:00
|
|
|
pa_sink_input_new_data data;
|
2011-09-29 18:54:03 +03:00
|
|
|
char *memblockq_name;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(ss);
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_assert(missing);
|
|
|
|
|
pa_assert(p);
|
2009-02-03 03:14:20 +01:00
|
|
|
pa_assert(ret);
|
2004-06-20 01:12:13 +00:00
|
|
|
|
2006-02-20 04:05:16 +00:00
|
|
|
/* Find syncid group */
|
2010-02-23 01:23:41 +01:00
|
|
|
PA_IDXSET_FOREACH(ssync, c->output_streams, idx) {
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
if (!playback_stream_isinstance(ssync))
|
2006-02-20 04:05:16 +00:00
|
|
|
continue;
|
|
|
|
|
|
2006-02-24 15:12:42 +00:00
|
|
|
if (ssync->syncid == syncid)
|
2006-02-20 04:05:16 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Synced streams must connect to the same sink */
|
2007-10-28 19:13:50 +00:00
|
|
|
if (ssync) {
|
|
|
|
|
|
|
|
|
|
if (!sink)
|
|
|
|
|
sink = ssync->sink_input->sink;
|
2009-02-03 03:14:20 +01:00
|
|
|
else if (sink != ssync->sink_input->sink) {
|
|
|
|
|
*ret = PA_ERR_INVALID;
|
2011-06-02 19:35:09 +05:30
|
|
|
goto out;
|
2009-02-03 03:14:20 +01:00
|
|
|
}
|
2007-10-28 19:13:50 +00:00
|
|
|
}
|
2006-08-13 16:19:56 +00:00
|
|
|
|
|
|
|
|
pa_sink_input_new_data_init(&data);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
|
|
|
|
pa_proplist_update(data.proplist, PA_UPDATE_REPLACE, p);
|
2006-08-13 16:19:56 +00:00
|
|
|
data.driver = __FILE__;
|
2008-08-03 16:44:38 +02:00
|
|
|
data.module = c->options->module;
|
2008-05-15 23:34:41 +00:00
|
|
|
data.client = c->client;
|
2011-02-28 13:23:23 +05:30
|
|
|
if (sink)
|
2017-11-28 19:50:01 +02:00
|
|
|
pa_sink_input_new_data_set_sink(&data, sink, false, true);
|
2011-02-28 13:23:23 +05:30
|
|
|
if (pa_sample_spec_valid(ss))
|
|
|
|
|
pa_sink_input_new_data_set_sample_spec(&data, ss);
|
|
|
|
|
if (pa_channel_map_valid(map))
|
|
|
|
|
pa_sink_input_new_data_set_channel_map(&data, map);
|
2011-06-02 19:35:09 +05:30
|
|
|
if (formats) {
|
2011-02-28 13:23:23 +05:30
|
|
|
pa_sink_input_new_data_set_formats(&data, formats);
|
2011-08-24 18:24:46 +02:00
|
|
|
/* Ownership transferred to new_data, so we don't free it ourselves */
|
2011-06-02 19:35:09 +05:30
|
|
|
formats = NULL;
|
|
|
|
|
}
|
2009-10-28 23:47:21 +01:00
|
|
|
if (volume) {
|
2009-02-04 18:34:08 +01:00
|
|
|
pa_sink_input_new_data_set_volume(&data, volume);
|
2009-10-29 00:46:22 +01:00
|
|
|
data.volume_is_absolute = !relative_volume;
|
2013-06-27 19:28:09 +02:00
|
|
|
data.save_volume = false;
|
2009-10-28 23:47:21 +01:00
|
|
|
}
|
2009-10-28 23:50:42 +01:00
|
|
|
if (muted_set) {
|
2008-10-26 19:32:04 +01:00
|
|
|
pa_sink_input_new_data_set_muted(&data, muted);
|
2013-06-27 19:28:09 +02:00
|
|
|
data.save_muted = false;
|
2009-10-28 23:50:42 +01:00
|
|
|
}
|
2007-10-28 19:13:50 +00:00
|
|
|
data.sync_base = ssync ? ssync->sink_input : NULL;
|
2009-08-28 23:24:09 +02:00
|
|
|
data.flags = flags;
|
2006-08-13 16:19:56 +00:00
|
|
|
|
2009-08-28 23:24:09 +02:00
|
|
|
*ret = -pa_sink_input_new(&sink_input, c->protocol->core, &data);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
|
|
|
|
pa_sink_input_new_data_done(&data);
|
|
|
|
|
|
|
|
|
|
if (!sink_input)
|
2011-06-02 19:35:09 +05:30
|
|
|
goto out;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
s = pa_msgobject_new(playback_stream);
|
|
|
|
|
s->parent.parent.parent.free = playback_stream_free;
|
|
|
|
|
s->parent.parent.process_msg = playback_stream_process_msg;
|
2004-06-20 01:12:13 +00:00
|
|
|
s->connection = c;
|
2006-02-20 04:05:16 +00:00
|
|
|
s->syncid = syncid;
|
2004-07-10 16:50:22 +00:00
|
|
|
s->sink_input = sink_input;
|
2013-06-27 19:28:09 +02:00
|
|
|
s->is_underrun = true;
|
|
|
|
|
s->drain_request = false;
|
2008-06-27 00:35:40 +02:00
|
|
|
pa_atomic_store(&s->missing, 0);
|
2011-06-14 16:38:02 -07:00
|
|
|
s->buffer_attr_req = *a;
|
2009-03-30 18:46:12 +02:00
|
|
|
s->adjust_latency = adjust_latency;
|
|
|
|
|
s->early_requests = early_requests;
|
2010-12-09 11:08:37 +01:00
|
|
|
pa_atomic_store(&s->seek_or_post_in_queue, 0);
|
|
|
|
|
s->seek_windex = -1;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
s->sink_input->parent.process_msg = sink_input_process_msg;
|
2008-05-15 23:34:41 +00:00
|
|
|
s->sink_input->pop = sink_input_pop_cb;
|
2013-03-01 11:51:54 +01:00
|
|
|
s->sink_input->process_underrun = sink_input_process_underrun_cb;
|
2008-05-15 23:34:41 +00:00
|
|
|
s->sink_input->process_rewind = sink_input_process_rewind_cb;
|
|
|
|
|
s->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
|
2008-06-28 01:09:07 +02:00
|
|
|
s->sink_input->update_max_request = sink_input_update_max_request_cb;
|
2004-06-23 23:17:30 +00:00
|
|
|
s->sink_input->kill = sink_input_kill_cb;
|
2009-03-30 18:21:34 +02:00
|
|
|
s->sink_input->moving = sink_input_moving_cb;
|
2007-11-21 01:30:40 +00:00
|
|
|
s->sink_input->suspend = sink_input_suspend_cb;
|
2009-02-12 03:18:05 +01:00
|
|
|
s->sink_input->send_event = sink_input_send_event_cb;
|
2004-06-23 23:17:30 +00:00
|
|
|
s->sink_input->userdata = s;
|
2004-06-20 01:12:13 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
start_index = ssync ? pa_memblockq_get_read_index(ssync->memblockq) : 0;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2009-03-30 18:46:12 +02:00
|
|
|
fix_playback_buffer_attr(s);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2009-03-30 18:46:12 +02:00
|
|
|
pa_sink_input_get_silence(sink_input, &silence);
|
2011-09-29 18:54:03 +03:00
|
|
|
memblockq_name = pa_sprintf_malloc("native protocol playback stream memblockq [%u]", s->sink_input->index);
|
2006-02-20 04:05:16 +00:00
|
|
|
s->memblockq = pa_memblockq_new(
|
2011-09-29 18:54:03 +03:00
|
|
|
memblockq_name,
|
2006-02-20 04:05:16 +00:00
|
|
|
start_index,
|
2009-03-30 18:46:12 +02:00
|
|
|
s->buffer_attr.maxlength,
|
|
|
|
|
s->buffer_attr.tlength,
|
2011-09-29 18:54:03 +03:00
|
|
|
&sink_input->sample_spec,
|
2009-03-30 18:46:12 +02:00
|
|
|
s->buffer_attr.prebuf,
|
|
|
|
|
s->buffer_attr.minreq,
|
2008-05-15 23:34:41 +00:00
|
|
|
0,
|
|
|
|
|
&silence);
|
2011-09-29 18:54:03 +03:00
|
|
|
pa_xfree(memblockq_name);
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_memblock_unref(silence.memblock);
|
2009-03-30 18:46:12 +02:00
|
|
|
|
|
|
|
|
pa_memblockq_get_attr(s->memblockq, &s->buffer_attr);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
*missing = (uint32_t) pa_memblockq_pop_missing(s->memblockq);
|
|
|
|
|
|
2011-11-10 12:43:09 +05:30
|
|
|
#ifdef PROTOCOL_NATIVE_DEBUG
|
|
|
|
|
pa_log("missing original: %li", (long int) *missing);
|
|
|
|
|
#endif
|
2010-01-08 20:07:34 +01:00
|
|
|
|
2007-11-21 01:30:40 +00:00
|
|
|
*ss = s->sink_input->sample_spec;
|
|
|
|
|
*map = s->sink_input->channel_map;
|
|
|
|
|
|
2004-08-03 19:26:56 +00:00
|
|
|
pa_idxset_put(c->output_streams, s, &s->index);
|
2006-02-20 04:05:16 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_log_info("Final latency %0.2f ms = %0.2f ms + 2*%0.2f ms + %0.2f ms",
|
2009-04-05 02:59:02 +02:00
|
|
|
((double) pa_bytes_to_usec(s->buffer_attr.tlength, &sink_input->sample_spec) + (double) s->configured_sink_latency) / PA_USEC_PER_MSEC,
|
2009-03-30 18:46:12 +02:00
|
|
|
(double) pa_bytes_to_usec(s->buffer_attr.tlength-s->buffer_attr.minreq*2, &sink_input->sample_spec) / PA_USEC_PER_MSEC,
|
|
|
|
|
(double) pa_bytes_to_usec(s->buffer_attr.minreq, &sink_input->sample_spec) / PA_USEC_PER_MSEC,
|
2009-04-05 02:59:02 +02:00
|
|
|
(double) s->configured_sink_latency / PA_USEC_PER_MSEC);
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_sink_input_put(s->sink_input);
|
2011-06-02 19:35:09 +05:30
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
if (formats)
|
2013-02-12 21:36:55 +02:00
|
|
|
pa_idxset_free(formats, (pa_free_cb_t) pa_format_info_free);
|
2011-06-02 19:35:09 +05:30
|
|
|
|
2004-06-20 01:12:13 +00:00
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-30 18:26:23 +02:00
|
|
|
/* Called from IO context */
|
2008-08-04 18:58:29 +02:00
|
|
|
static void playback_stream_request_bytes(playback_stream *s) {
|
2016-05-19 15:26:08 +02:00
|
|
|
size_t m;
|
2008-08-04 18:58:29 +02:00
|
|
|
|
|
|
|
|
playback_stream_assert_ref(s);
|
|
|
|
|
|
|
|
|
|
m = pa_memblockq_pop_missing(s->memblockq);
|
|
|
|
|
|
2010-01-08 20:07:34 +01:00
|
|
|
/* pa_log("request_bytes(%lu) (tlength=%lu minreq=%lu length=%lu really missing=%lli)", */
|
2009-08-15 00:54:51 +02:00
|
|
|
/* (unsigned long) m, */
|
|
|
|
|
/* pa_memblockq_get_tlength(s->memblockq), */
|
|
|
|
|
/* pa_memblockq_get_minreq(s->memblockq), */
|
2010-01-08 20:07:34 +01:00
|
|
|
/* pa_memblockq_get_length(s->memblockq), */
|
|
|
|
|
/* (long long) pa_memblockq_get_tlength(s->memblockq) - (long long) pa_memblockq_get_length(s->memblockq)); */
|
2009-08-15 00:54:51 +02:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
if (m <= 0)
|
|
|
|
|
return;
|
|
|
|
|
|
2011-11-10 12:43:09 +05:30
|
|
|
#ifdef PROTOCOL_NATIVE_DEBUG
|
|
|
|
|
pa_log("request_bytes(%lu)", (unsigned long) m);
|
|
|
|
|
#endif
|
2008-08-04 18:58:29 +02:00
|
|
|
|
2016-05-19 15:26:08 +02:00
|
|
|
if (pa_atomic_add(&s->missing, (int) m) <= 0)
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-30 18:26:23 +02:00
|
|
|
/* Called from main context */
|
2008-08-04 18:58:29 +02:00
|
|
|
static void playback_stream_send_killed(playback_stream *p) {
|
|
|
|
|
pa_tagstruct *t;
|
|
|
|
|
playback_stream_assert_ref(p);
|
|
|
|
|
|
2014-10-23 16:09:45 +02:00
|
|
|
t = pa_tagstruct_new();
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_KILLED);
|
|
|
|
|
pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
|
|
|
|
|
pa_tagstruct_putu32(t, p->index);
|
|
|
|
|
pa_pstream_send_tagstruct(p->connection->pstream, t);
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-30 18:26:23 +02:00
|
|
|
/* Called from main context */
|
2008-08-04 18:58:29 +02:00
|
|
|
static int native_connection_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
|
|
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(o);
|
|
|
|
|
pa_native_connection_assert_ref(c);
|
2004-06-20 01:12:13 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
if (!c->protocol)
|
|
|
|
|
return -1;
|
2006-02-20 04:05:16 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
switch (code) {
|
2004-07-07 22:02:15 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
case CONNECTION_MESSAGE_REVOKE:
|
|
|
|
|
pa_pstream_send_revoke(c->pstream, PA_PTR_TO_UINT(userdata));
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case CONNECTION_MESSAGE_RELEASE:
|
|
|
|
|
pa_pstream_send_release(c->pstream, PA_PTR_TO_UINT(userdata));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
2004-06-20 01:12:13 +00:00
|
|
|
}
|
|
|
|
|
|
2009-03-30 18:26:23 +02:00
|
|
|
/* Called from main context */
|
2008-08-04 18:58:29 +02:00
|
|
|
static void native_connection_unlink(pa_native_connection *c) {
|
2007-10-28 19:13:50 +00:00
|
|
|
record_stream *r;
|
|
|
|
|
output_stream *o;
|
|
|
|
|
|
|
|
|
|
pa_assert(c);
|
|
|
|
|
|
|
|
|
|
if (!c->protocol)
|
|
|
|
|
return;
|
2004-06-20 01:12:13 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_hook_fire(&c->protocol->hooks[PA_NATIVE_HOOK_CONNECTION_UNLINK], c);
|
|
|
|
|
|
2008-08-03 16:44:38 +02:00
|
|
|
if (c->options)
|
|
|
|
|
pa_native_options_unref(c->options);
|
|
|
|
|
|
2014-04-25 17:57:18 +02:00
|
|
|
if (c->srbpending)
|
|
|
|
|
pa_srbchannel_free(c->srbpending);
|
|
|
|
|
|
2004-07-03 23:35:12 +00:00
|
|
|
while ((r = pa_idxset_first(c->record_streams, NULL)))
|
2007-10-28 19:13:50 +00:00
|
|
|
record_stream_unlink(r);
|
2004-06-20 01:12:13 +00:00
|
|
|
|
2004-08-03 19:26:56 +00:00
|
|
|
while ((o = pa_idxset_first(c->output_streams, NULL)))
|
2007-10-28 19:13:50 +00:00
|
|
|
if (playback_stream_isinstance(o))
|
|
|
|
|
playback_stream_unlink(PLAYBACK_STREAM(o));
|
2004-08-03 19:26:56 +00:00
|
|
|
else
|
2007-10-28 19:13:50 +00:00
|
|
|
upload_stream_unlink(UPLOAD_STREAM(o));
|
2004-08-11 15:11:26 +00:00
|
|
|
|
|
|
|
|
if (c->subscription)
|
|
|
|
|
pa_subscription_free(c->subscription);
|
2004-11-18 00:28:26 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
if (c->pstream)
|
|
|
|
|
pa_pstream_unlink(c->pstream);
|
|
|
|
|
|
|
|
|
|
if (c->auth_timeout_event) {
|
2004-11-18 00:28:26 +00:00
|
|
|
c->protocol->core->mainloop->time_free(c->auth_timeout_event);
|
2007-10-28 19:13:50 +00:00
|
|
|
c->auth_timeout_event = NULL;
|
|
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert_se(pa_idxset_remove_by_data(c->protocol->connections, c, NULL) == c);
|
|
|
|
|
c->protocol = NULL;
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_unref(c);
|
2004-06-20 01:12:13 +00:00
|
|
|
}
|
|
|
|
|
|
2009-03-30 18:26:23 +02:00
|
|
|
/* Called from main context */
|
2008-08-04 18:58:29 +02:00
|
|
|
static void native_connection_free(pa_object *o) {
|
|
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(o);
|
2004-06-20 01:12:13 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(c);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
native_connection_unlink(c);
|
2004-06-27 17:50:02 +00:00
|
|
|
|
2013-02-12 21:36:55 +02:00
|
|
|
pa_idxset_free(c->record_streams, NULL);
|
|
|
|
|
pa_idxset_free(c->output_streams, NULL);
|
2004-07-07 00:22:46 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_pdispatch_unref(c->pdispatch);
|
|
|
|
|
pa_pstream_unref(c->pstream);
|
2016-03-13 00:53:34 +02:00
|
|
|
if (c->rw_mempool)
|
|
|
|
|
pa_mempool_unref(c->rw_mempool);
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_client_free(c->client);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_xfree(c);
|
|
|
|
|
}
|
2004-06-27 17:50:02 +00:00
|
|
|
|
2009-03-30 18:26:23 +02:00
|
|
|
/* Called from main context */
|
2008-08-04 18:58:29 +02:00
|
|
|
static void native_connection_send_memblock(pa_native_connection *c) {
|
2004-07-10 16:50:22 +00:00
|
|
|
uint32_t start;
|
2007-10-28 19:13:50 +00:00
|
|
|
record_stream *r;
|
2004-07-10 16:50:22 +00:00
|
|
|
|
|
|
|
|
start = PA_IDXSET_INVALID;
|
|
|
|
|
for (;;) {
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_memchunk chunk;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
if (!(r = RECORD_STREAM(pa_idxset_rrobin(c->record_streams, &c->rrobin_index))))
|
2004-07-10 16:50:22 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (start == PA_IDXSET_INVALID)
|
|
|
|
|
start = c->rrobin_index;
|
|
|
|
|
else if (start == c->rrobin_index)
|
|
|
|
|
return;
|
|
|
|
|
|
2011-03-12 19:45:02 +01:00
|
|
|
if (pa_memblockq_peek(r->memblockq, &chunk) >= 0) {
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_memchunk schunk = chunk;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2009-03-30 18:46:12 +02:00
|
|
|
if (schunk.length > r->buffer_attr.fragsize)
|
|
|
|
|
schunk.length = r->buffer_attr.fragsize;
|
2004-07-10 16:50:22 +00:00
|
|
|
|
2006-02-20 04:05:16 +00:00
|
|
|
pa_pstream_send_memblock(c->pstream, r->index, 0, PA_SEEK_RELATIVE, &schunk);
|
2007-10-28 19:13:50 +00:00
|
|
|
|
|
|
|
|
pa_memblockq_drop(r->memblockq, schunk.length);
|
2004-09-06 18:55:47 +00:00
|
|
|
pa_memblock_unref(schunk.memblock);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-07-10 16:50:22 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
/*** sink input callbacks ***/
|
2004-06-23 23:17:30 +00:00
|
|
|
|
2009-03-30 18:26:23 +02:00
|
|
|
/* Called from thread context */
|
2008-05-15 23:34:41 +00:00
|
|
|
static void handle_seek(playback_stream *s, int64_t indexw) {
|
|
|
|
|
playback_stream_assert_ref(s);
|
|
|
|
|
|
|
|
|
|
/* pa_log("handle_seek: %llu -- %i", (unsigned long long) s->sink_input->thread_info.underrun_for, pa_memblockq_is_readable(s->memblockq)); */
|
|
|
|
|
|
|
|
|
|
if (s->sink_input->thread_info.underrun_for > 0) {
|
|
|
|
|
|
|
|
|
|
/* pa_log("%lu vs. %lu", (unsigned long) pa_memblockq_get_length(s->memblockq), (unsigned long) pa_memblockq_get_prebuf(s->memblockq)); */
|
|
|
|
|
|
|
|
|
|
if (pa_memblockq_is_readable(s->memblockq)) {
|
|
|
|
|
|
|
|
|
|
/* We just ended an underrun, let's ask the sink
|
|
|
|
|
* for a complete rewind rewrite */
|
|
|
|
|
|
|
|
|
|
pa_log_debug("Requesting rewind due to end of underrun.");
|
|
|
|
|
pa_sink_input_request_rewind(s->sink_input,
|
2009-08-27 00:04:33 +02:00
|
|
|
(size_t) (s->sink_input->thread_info.underrun_for == (uint64_t) -1 ? 0 :
|
|
|
|
|
s->sink_input->thread_info.underrun_for),
|
2013-06-27 19:28:09 +02:00
|
|
|
false, true, false);
|
2008-05-15 23:34:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
int64_t indexr;
|
|
|
|
|
|
|
|
|
|
indexr = pa_memblockq_get_read_index(s->memblockq);
|
|
|
|
|
|
|
|
|
|
if (indexw < indexr) {
|
|
|
|
|
/* OK, the sink already asked for this data, so
|
2011-08-24 18:24:46 +02:00
|
|
|
* let's have it ask us again */
|
2008-05-15 23:34:41 +00:00
|
|
|
|
|
|
|
|
pa_log_debug("Requesting rewind due to rewrite.");
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_sink_input_request_rewind(s->sink_input, (size_t) (indexr - indexw), true, false, false);
|
2008-05-15 23:34:41 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
playback_stream_request_bytes(s);
|
2008-05-15 23:34:41 +00:00
|
|
|
}
|
|
|
|
|
|
2010-01-18 01:33:04 +01:00
|
|
|
static void flush_write_no_account(pa_memblockq *q) {
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_memblockq_flush_write(q, false);
|
2010-01-18 01:33:04 +01:00
|
|
|
}
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
/* Called from thread context */
|
|
|
|
|
static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
|
|
|
|
|
pa_sink_input *i = PA_SINK_INPUT(o);
|
|
|
|
|
playback_stream *s;
|
2004-06-23 23:17:30 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_sink_input_assert_ref(i);
|
|
|
|
|
s = PLAYBACK_STREAM(i->userdata);
|
|
|
|
|
playback_stream_assert_ref(s);
|
|
|
|
|
|
|
|
|
|
switch (code) {
|
|
|
|
|
|
2010-12-09 11:08:37 +01:00
|
|
|
case SINK_INPUT_MESSAGE_SEEK:
|
2007-10-28 19:13:50 +00:00
|
|
|
case SINK_INPUT_MESSAGE_POST_DATA: {
|
2010-12-09 11:08:37 +01:00
|
|
|
int64_t windex = pa_memblockq_get_write_index(s->memblockq);
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2010-12-09 11:08:37 +01:00
|
|
|
if (code == SINK_INPUT_MESSAGE_SEEK) {
|
|
|
|
|
/* The client side is incapable of accounting correctly
|
|
|
|
|
* for seeks of a type != PA_SEEK_RELATIVE. We need to be
|
|
|
|
|
* able to deal with that. */
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2010-12-09 11:08:37 +01:00
|
|
|
pa_memblockq_seek(s->memblockq, offset, PA_PTR_TO_UINT(userdata), PA_PTR_TO_UINT(userdata) == PA_SEEK_RELATIVE);
|
|
|
|
|
windex = PA_MIN(windex, pa_memblockq_get_write_index(s->memblockq));
|
|
|
|
|
}
|
2009-08-15 00:16:25 +02:00
|
|
|
|
2010-12-09 11:08:37 +01:00
|
|
|
if (chunk && pa_memblockq_push_align(s->memblockq, chunk) < 0) {
|
2011-01-22 01:08:36 +01:00
|
|
|
if (pa_log_ratelimit(PA_LOG_WARN))
|
2009-08-15 00:16:25 +02:00
|
|
|
pa_log_warn("Failed to push data into queue");
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_OVERFLOW, NULL, 0, NULL, NULL);
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_memblockq_seek(s->memblockq, (int64_t) chunk->length, PA_SEEK_RELATIVE, true);
|
2007-10-28 19:13:50 +00:00
|
|
|
}
|
2006-02-20 04:05:16 +00:00
|
|
|
|
2010-12-09 11:08:37 +01:00
|
|
|
/* If more data is in queue, we rewind later instead. */
|
|
|
|
|
if (s->seek_windex != -1)
|
2011-03-02 12:41:26 +01:00
|
|
|
windex = PA_MIN(windex, s->seek_windex);
|
2010-12-09 11:08:37 +01:00
|
|
|
if (pa_atomic_dec(&s->seek_or_post_in_queue) > 1)
|
|
|
|
|
s->seek_windex = windex;
|
|
|
|
|
else {
|
|
|
|
|
s->seek_windex = -1;
|
|
|
|
|
handle_seek(s, windex);
|
|
|
|
|
}
|
2007-10-28 19:13:50 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
case SINK_INPUT_MESSAGE_DRAIN:
|
2007-10-28 19:13:50 +00:00
|
|
|
case SINK_INPUT_MESSAGE_FLUSH:
|
|
|
|
|
case SINK_INPUT_MESSAGE_PREBUF_FORCE:
|
|
|
|
|
case SINK_INPUT_MESSAGE_TRIGGER: {
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
int64_t windex;
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_sink_input *isync;
|
|
|
|
|
void (*func)(pa_memblockq *bq);
|
|
|
|
|
|
2011-03-02 12:41:26 +01:00
|
|
|
switch (code) {
|
2007-10-28 19:13:50 +00:00
|
|
|
case SINK_INPUT_MESSAGE_FLUSH:
|
2010-01-18 01:33:04 +01:00
|
|
|
func = flush_write_no_account;
|
2007-10-28 19:13:50 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SINK_INPUT_MESSAGE_PREBUF_FORCE:
|
|
|
|
|
func = pa_memblockq_prebuf_force;
|
|
|
|
|
break;
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
case SINK_INPUT_MESSAGE_DRAIN:
|
2007-10-28 19:13:50 +00:00
|
|
|
case SINK_INPUT_MESSAGE_TRIGGER:
|
|
|
|
|
func = pa_memblockq_prebuf_disable;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
pa_assert_not_reached();
|
|
|
|
|
}
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
windex = pa_memblockq_get_write_index(s->memblockq);
|
2007-10-28 19:13:50 +00:00
|
|
|
func(s->memblockq);
|
2008-05-15 23:34:41 +00:00
|
|
|
handle_seek(s, windex);
|
2007-10-28 19:13:50 +00:00
|
|
|
|
|
|
|
|
/* Do the same for all other members in the sync group */
|
|
|
|
|
for (isync = i->sync_prev; isync; isync = isync->sync_prev) {
|
|
|
|
|
playback_stream *ssync = PLAYBACK_STREAM(isync->userdata);
|
2008-05-15 23:34:41 +00:00
|
|
|
windex = pa_memblockq_get_write_index(ssync->memblockq);
|
2007-10-28 19:13:50 +00:00
|
|
|
func(ssync->memblockq);
|
2008-05-15 23:34:41 +00:00
|
|
|
handle_seek(ssync, windex);
|
2007-10-28 19:13:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (isync = i->sync_next; isync; isync = isync->sync_next) {
|
|
|
|
|
playback_stream *ssync = PLAYBACK_STREAM(isync->userdata);
|
2008-05-15 23:34:41 +00:00
|
|
|
windex = pa_memblockq_get_write_index(ssync->memblockq);
|
2007-10-28 19:13:50 +00:00
|
|
|
func(ssync->memblockq);
|
2008-05-15 23:34:41 +00:00
|
|
|
handle_seek(ssync, windex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (code == SINK_INPUT_MESSAGE_DRAIN) {
|
|
|
|
|
if (!pa_memblockq_is_readable(s->memblockq))
|
|
|
|
|
pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_DRAIN_ACK, userdata, 0, NULL, NULL);
|
|
|
|
|
else {
|
|
|
|
|
s->drain_tag = PA_PTR_TO_UINT(userdata);
|
2013-06-27 19:28:09 +02:00
|
|
|
s->drain_request = true;
|
2008-05-15 23:34:41 +00:00
|
|
|
}
|
2007-10-28 19:13:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case SINK_INPUT_MESSAGE_UPDATE_LATENCY:
|
2009-04-05 02:59:02 +02:00
|
|
|
/* Atomically get a snapshot of all timing parameters... */
|
2007-10-28 19:13:50 +00:00
|
|
|
s->read_index = pa_memblockq_get_read_index(s->memblockq);
|
|
|
|
|
s->write_index = pa_memblockq_get_write_index(s->memblockq);
|
2008-05-15 23:34:41 +00:00
|
|
|
s->render_memblockq_length = pa_memblockq_get_length(s->sink_input->thread_info.render_memblockq);
|
source/sink: Allow pa_{source, sink}_get_latency_within_thread() to return negative values
The reported latency of source or sink is based on measured initial conditions.
If the conditions contain an error, the estimated latency values may become negative.
This does not indicate that the latency is indeed negative but can be considered
merely an offset error. The current get_latency_in_thread() calls and the
implementations of the PA_{SINK,SOURCE}_MESSAGE_GET_LATENCY messages truncate negative
latencies because they do not make sense from a physical point of view. In fact,
the values are truncated twice, once in the message handler and a second time in
the pa_{source,sink}_get_latency_within_thread() call itself.
This leads to two problems for the latency controller within module-loopback:
- Truncating leads to discontinuities in the latency reports which then trigger
unwanted end to end latency corrections.
- If a large negative port latency offsets is set, the reported latency is always 0,
making it impossible to control the end to end latency at all.
This patch is a pre-condition for solving these problems.
It adds a new flag to pa_{sink,source}_get_latency_within_thread() to allow
negative return values. Truncating is also removed in all implementations of the
PA_{SINK,SOURCE}_MESSAGE_GET_LATENCY message handlers. The allow_negative flag
is set to false for all calls of pa_{sink,source}_get_latency_within_thread()
except when used within PA_{SINK,SOURCE}_MESSAGE_GET_LATENCY. This means that the
original behavior is not altered in most cases. Only if a positive latency offset
is set and the message returns a negative value, the reported latency is smaller
because the values are not truncated twice.
Additionally let PA_SOURCE_MESSAGE_GET_LATENCY return -pa_sink_get_latency_within_thread()
for monitor sources because the source gets the data before it is played.
2017-04-17 19:50:10 +02:00
|
|
|
s->current_sink_latency = pa_sink_get_latency_within_thread(s->sink_input->sink, false);
|
2009-04-05 02:59:02 +02:00
|
|
|
s->underrun_for = s->sink_input->thread_info.underrun_for;
|
|
|
|
|
s->playing_for = s->sink_input->thread_info.playing_for;
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
return 0;
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
case PA_SINK_INPUT_MESSAGE_SET_STATE: {
|
|
|
|
|
int64_t windex;
|
|
|
|
|
|
|
|
|
|
windex = pa_memblockq_get_write_index(s->memblockq);
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2013-05-07 18:00:34 +03:00
|
|
|
/* We enable prebuffering so that after CORKED -> RUNNING
|
|
|
|
|
* transitions we don't have trouble with underruns in case the
|
|
|
|
|
* buffer has too little data. This must not be done when draining
|
|
|
|
|
* has been requested, however, otherwise the buffered audio would
|
|
|
|
|
* never play. */
|
|
|
|
|
if (!s->drain_request)
|
|
|
|
|
pa_memblockq_prebuf_force(s->memblockq);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
|
|
|
|
handle_seek(s, windex);
|
|
|
|
|
|
|
|
|
|
/* Fall through to the default handler */
|
2007-10-28 19:13:50 +00:00
|
|
|
break;
|
2008-05-15 23:34:41 +00:00
|
|
|
}
|
2007-10-28 19:13:50 +00:00
|
|
|
|
|
|
|
|
case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
|
|
|
|
|
pa_usec_t *r = userdata;
|
|
|
|
|
|
|
|
|
|
*r = pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &i->sample_spec);
|
|
|
|
|
|
|
|
|
|
/* Fall through, the default handler will add in the extra
|
|
|
|
|
* latency added by the resampler */
|
|
|
|
|
break;
|
|
|
|
|
}
|
2009-03-30 18:46:12 +02:00
|
|
|
|
|
|
|
|
case SINK_INPUT_MESSAGE_UPDATE_BUFFER_ATTR: {
|
|
|
|
|
pa_memblockq_apply_attr(s->memblockq, &s->buffer_attr);
|
|
|
|
|
pa_memblockq_get_attr(s->memblockq, &s->buffer_attr);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2007-10-28 19:13:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return pa_sink_input_process_msg(o, code, userdata, offset, chunk);
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-18 22:24:24 +02:00
|
|
|
static bool handle_input_underrun(playback_stream *s, bool force) {
|
2013-03-01 11:51:54 +01:00
|
|
|
bool send_drain;
|
|
|
|
|
|
|
|
|
|
if (pa_memblockq_is_readable(s->memblockq))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (!s->is_underrun)
|
|
|
|
|
pa_log_debug("%s %s of '%s'", force ? "Actual" : "Implicit",
|
|
|
|
|
s->drain_request ? "drain" : "underrun", pa_strnull(pa_proplist_gets(s->sink_input->proplist, PA_PROP_MEDIA_NAME)));
|
|
|
|
|
|
|
|
|
|
send_drain = s->drain_request && (force || pa_sink_input_safe_to_remove(s->sink_input));
|
|
|
|
|
|
|
|
|
|
if (send_drain) {
|
|
|
|
|
s->drain_request = false;
|
|
|
|
|
pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_DRAIN_ACK, PA_UINT_TO_PTR(s->drain_tag), 0, NULL, NULL);
|
|
|
|
|
pa_log_debug("Drain acknowledged of '%s'", pa_strnull(pa_proplist_gets(s->sink_input->proplist, PA_PROP_MEDIA_NAME)));
|
|
|
|
|
} else if (!s->is_underrun) {
|
|
|
|
|
pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_UNDERFLOW, NULL, pa_memblockq_get_read_index(s->memblockq), NULL, NULL);
|
|
|
|
|
}
|
|
|
|
|
s->is_underrun = true;
|
|
|
|
|
playback_stream_request_bytes(s);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Called from thread context */
|
|
|
|
|
static bool sink_input_process_underrun_cb(pa_sink_input *i) {
|
|
|
|
|
playback_stream *s;
|
|
|
|
|
|
|
|
|
|
pa_sink_input_assert_ref(i);
|
|
|
|
|
s = PLAYBACK_STREAM(i->userdata);
|
|
|
|
|
playback_stream_assert_ref(s);
|
|
|
|
|
|
|
|
|
|
return handle_input_underrun(s, true);
|
|
|
|
|
}
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
/* Called from thread context */
|
2008-05-15 23:34:41 +00:00
|
|
|
static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
|
2007-10-28 19:13:50 +00:00
|
|
|
playback_stream *s;
|
|
|
|
|
|
|
|
|
|
pa_sink_input_assert_ref(i);
|
|
|
|
|
s = PLAYBACK_STREAM(i->userdata);
|
|
|
|
|
playback_stream_assert_ref(s);
|
|
|
|
|
pa_assert(chunk);
|
|
|
|
|
|
2011-11-10 12:43:09 +05:30
|
|
|
#ifdef PROTOCOL_NATIVE_DEBUG
|
|
|
|
|
pa_log("%s, pop(): %lu", pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME), (unsigned long) pa_memblockq_get_length(s->memblockq));
|
|
|
|
|
#endif
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2013-03-01 11:51:54 +01:00
|
|
|
if (!handle_input_underrun(s, false))
|
|
|
|
|
s->is_underrun = false;
|
2004-06-23 23:17:30 +00:00
|
|
|
|
2008-06-27 00:35:40 +02:00
|
|
|
/* This call will not fail with prebuf=0, hence we check for
|
2013-03-01 11:51:54 +01:00
|
|
|
underrun explicitly in handle_input_underrun */
|
2008-06-27 00:35:40 +02:00
|
|
|
if (pa_memblockq_peek(s->memblockq, chunk) < 0)
|
|
|
|
|
return -1;
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2008-05-27 22:07:27 +00:00
|
|
|
chunk->length = PA_MIN(nbytes, chunk->length);
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (i->thread_info.underrun_for > 0)
|
|
|
|
|
pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_STARTED, NULL, 0, NULL, NULL);
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_memblockq_drop(s->memblockq, chunk->length);
|
2008-08-04 18:58:29 +02:00
|
|
|
playback_stream_request_bytes(s);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-06-23 23:17:30 +00:00
|
|
|
return 0;
|
2004-06-20 01:12:13 +00:00
|
|
|
}
|
|
|
|
|
|
2009-03-30 18:26:23 +02:00
|
|
|
/* Called from thread context */
|
2008-05-15 23:34:41 +00:00
|
|
|
static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
|
2007-10-28 19:13:50 +00:00
|
|
|
playback_stream *s;
|
2004-06-20 01:12:13 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_sink_input_assert_ref(i);
|
|
|
|
|
s = PLAYBACK_STREAM(i->userdata);
|
|
|
|
|
playback_stream_assert_ref(s);
|
2006-02-20 04:05:16 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
/* If we are in an underrun, then we don't rewind */
|
|
|
|
|
if (i->thread_info.underrun_for > 0)
|
|
|
|
|
return;
|
2004-07-07 22:02:15 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_memblockq_rewind(s->memblockq, nbytes);
|
|
|
|
|
}
|
2004-08-27 01:29:49 +00:00
|
|
|
|
2009-03-30 18:26:23 +02:00
|
|
|
/* Called from thread context */
|
2008-05-15 23:34:41 +00:00
|
|
|
static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
|
|
|
|
|
playback_stream *s;
|
|
|
|
|
|
|
|
|
|
pa_sink_input_assert_ref(i);
|
|
|
|
|
s = PLAYBACK_STREAM(i->userdata);
|
|
|
|
|
playback_stream_assert_ref(s);
|
2004-06-23 23:17:30 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_memblockq_set_maxrewind(s->memblockq, nbytes);
|
2004-06-23 23:17:30 +00:00
|
|
|
}
|
|
|
|
|
|
2009-03-30 18:26:23 +02:00
|
|
|
/* Called from thread context */
|
2008-06-28 01:09:07 +02:00
|
|
|
static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
|
|
|
|
|
playback_stream *s;
|
2009-03-31 21:36:45 +02:00
|
|
|
size_t new_tlength, old_tlength;
|
2008-06-28 01:09:07 +02:00
|
|
|
|
|
|
|
|
pa_sink_input_assert_ref(i);
|
|
|
|
|
s = PLAYBACK_STREAM(i->userdata);
|
|
|
|
|
playback_stream_assert_ref(s);
|
|
|
|
|
|
2009-03-31 21:36:45 +02:00
|
|
|
old_tlength = pa_memblockq_get_tlength(s->memblockq);
|
|
|
|
|
new_tlength = nbytes+2*pa_memblockq_get_minreq(s->memblockq);
|
2008-06-28 01:09:07 +02:00
|
|
|
|
2009-03-31 21:36:45 +02:00
|
|
|
if (old_tlength < new_tlength) {
|
|
|
|
|
pa_log_debug("max_request changed, trying to update from %zu to %zu.", old_tlength, new_tlength);
|
|
|
|
|
pa_memblockq_set_tlength(s->memblockq, new_tlength);
|
|
|
|
|
new_tlength = pa_memblockq_get_tlength(s->memblockq);
|
|
|
|
|
|
|
|
|
|
if (new_tlength == old_tlength)
|
|
|
|
|
pa_log_debug("Failed to increase tlength");
|
|
|
|
|
else {
|
|
|
|
|
pa_log_debug("Notifying client about increased tlength");
|
|
|
|
|
pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_UPDATE_TLENGTH, NULL, pa_memblockq_get_tlength(s->memblockq), NULL, NULL);
|
|
|
|
|
}
|
2009-03-30 18:46:12 +02:00
|
|
|
}
|
2008-06-28 01:09:07 +02:00
|
|
|
}
|
|
|
|
|
|
2007-11-21 01:30:40 +00:00
|
|
|
/* Called from main context */
|
2007-10-28 19:13:50 +00:00
|
|
|
static void sink_input_kill_cb(pa_sink_input *i) {
|
|
|
|
|
playback_stream *s;
|
2004-06-23 23:17:30 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_sink_input_assert_ref(i);
|
|
|
|
|
s = PLAYBACK_STREAM(i->userdata);
|
|
|
|
|
playback_stream_assert_ref(s);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
playback_stream_send_killed(s);
|
2007-10-28 19:13:50 +00:00
|
|
|
playback_stream_unlink(s);
|
2004-06-23 23:17:30 +00:00
|
|
|
}
|
|
|
|
|
|
2009-02-12 03:18:05 +01:00
|
|
|
/* Called from main context */
|
|
|
|
|
static void sink_input_send_event_cb(pa_sink_input *i, const char *event, pa_proplist *pl) {
|
|
|
|
|
playback_stream *s;
|
|
|
|
|
pa_tagstruct *t;
|
|
|
|
|
|
|
|
|
|
pa_sink_input_assert_ref(i);
|
|
|
|
|
s = PLAYBACK_STREAM(i->userdata);
|
|
|
|
|
playback_stream_assert_ref(s);
|
|
|
|
|
|
|
|
|
|
if (s->connection->version < 15)
|
|
|
|
|
return;
|
|
|
|
|
|
2014-10-23 16:09:45 +02:00
|
|
|
t = pa_tagstruct_new();
|
2009-02-12 03:18:05 +01:00
|
|
|
pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_EVENT);
|
|
|
|
|
pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
|
|
|
|
|
pa_tagstruct_putu32(t, s->index);
|
|
|
|
|
pa_tagstruct_puts(t, event);
|
|
|
|
|
pa_tagstruct_put_proplist(t, pl);
|
|
|
|
|
pa_pstream_send_tagstruct(s->connection->pstream, t);
|
|
|
|
|
}
|
|
|
|
|
|
2007-11-21 01:30:40 +00:00
|
|
|
/* Called from main context */
|
2019-02-12 20:21:01 +01:00
|
|
|
static void sink_input_suspend_cb(pa_sink_input *i, pa_sink_state_t old_state, pa_suspend_cause_t old_suspend_cause) {
|
2007-11-21 01:30:40 +00:00
|
|
|
playback_stream *s;
|
|
|
|
|
pa_tagstruct *t;
|
2019-02-12 20:21:01 +01:00
|
|
|
bool suspend;
|
2007-11-21 01:30:40 +00:00
|
|
|
|
|
|
|
|
pa_sink_input_assert_ref(i);
|
2019-02-12 20:21:01 +01:00
|
|
|
|
|
|
|
|
/* State has not changed, nothing to do */
|
|
|
|
|
if (old_state == i->sink->state)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
suspend = (i->sink->state == PA_SINK_SUSPENDED);
|
|
|
|
|
|
2007-11-21 01:30:40 +00:00
|
|
|
s = PLAYBACK_STREAM(i->userdata);
|
|
|
|
|
playback_stream_assert_ref(s);
|
|
|
|
|
|
2008-01-04 14:59:09 +00:00
|
|
|
if (s->connection->version < 12)
|
|
|
|
|
return;
|
|
|
|
|
|
2014-10-23 16:09:45 +02:00
|
|
|
t = pa_tagstruct_new();
|
2007-11-21 01:30:40 +00:00
|
|
|
pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_SUSPENDED);
|
|
|
|
|
pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
|
|
|
|
|
pa_tagstruct_putu32(t, s->index);
|
|
|
|
|
pa_tagstruct_put_boolean(t, suspend);
|
|
|
|
|
pa_pstream_send_tagstruct(s->connection->pstream, t);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Called from main context */
|
2009-04-01 03:04:39 +02:00
|
|
|
static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
|
2007-11-21 01:30:40 +00:00
|
|
|
playback_stream *s;
|
|
|
|
|
pa_tagstruct *t;
|
|
|
|
|
|
|
|
|
|
pa_sink_input_assert_ref(i);
|
|
|
|
|
s = PLAYBACK_STREAM(i->userdata);
|
|
|
|
|
playback_stream_assert_ref(s);
|
|
|
|
|
|
2009-08-15 00:05:17 +02:00
|
|
|
if (!dest)
|
|
|
|
|
return;
|
|
|
|
|
|
2009-03-30 18:46:12 +02:00
|
|
|
fix_playback_buffer_attr(s);
|
|
|
|
|
pa_memblockq_apply_attr(s->memblockq, &s->buffer_attr);
|
|
|
|
|
pa_memblockq_get_attr(s->memblockq, &s->buffer_attr);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2008-01-04 14:59:09 +00:00
|
|
|
if (s->connection->version < 12)
|
|
|
|
|
return;
|
|
|
|
|
|
2014-10-23 16:09:45 +02:00
|
|
|
t = pa_tagstruct_new();
|
2007-11-21 01:30:40 +00:00
|
|
|
pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_MOVED);
|
|
|
|
|
pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
|
|
|
|
|
pa_tagstruct_putu32(t, s->index);
|
2009-04-01 03:04:39 +02:00
|
|
|
pa_tagstruct_putu32(t, dest->index);
|
|
|
|
|
pa_tagstruct_puts(t, dest->name);
|
2018-06-26 16:25:58 +03:00
|
|
|
pa_tagstruct_put_boolean(t, dest->state == PA_SINK_SUSPENDED);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
|
|
|
|
if (s->connection->version >= 13) {
|
2009-03-30 18:46:12 +02:00
|
|
|
pa_tagstruct_putu32(t, s->buffer_attr.maxlength);
|
|
|
|
|
pa_tagstruct_putu32(t, s->buffer_attr.tlength);
|
|
|
|
|
pa_tagstruct_putu32(t, s->buffer_attr.prebuf);
|
|
|
|
|
pa_tagstruct_putu32(t, s->buffer_attr.minreq);
|
2009-04-05 02:59:02 +02:00
|
|
|
pa_tagstruct_put_usec(t, s->configured_sink_latency);
|
2008-05-15 23:34:41 +00:00
|
|
|
}
|
|
|
|
|
|
2007-11-21 01:30:40 +00:00
|
|
|
pa_pstream_send_tagstruct(s->connection->pstream, t);
|
|
|
|
|
}
|
|
|
|
|
|
2004-07-10 16:50:22 +00:00
|
|
|
/*** source_output callbacks ***/
|
|
|
|
|
|
2009-04-05 02:59:02 +02:00
|
|
|
/* Called from thread context */
|
|
|
|
|
static int source_output_process_msg(pa_msgobject *_o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
|
|
|
|
|
pa_source_output *o = PA_SOURCE_OUTPUT(_o);
|
|
|
|
|
record_stream *s;
|
|
|
|
|
|
|
|
|
|
pa_source_output_assert_ref(o);
|
|
|
|
|
s = RECORD_STREAM(o->userdata);
|
|
|
|
|
record_stream_assert_ref(s);
|
|
|
|
|
|
|
|
|
|
switch (code) {
|
|
|
|
|
case SOURCE_OUTPUT_MESSAGE_UPDATE_LATENCY:
|
|
|
|
|
/* Atomically get a snapshot of all timing parameters... */
|
source/sink: Allow pa_{source, sink}_get_latency_within_thread() to return negative values
The reported latency of source or sink is based on measured initial conditions.
If the conditions contain an error, the estimated latency values may become negative.
This does not indicate that the latency is indeed negative but can be considered
merely an offset error. The current get_latency_in_thread() calls and the
implementations of the PA_{SINK,SOURCE}_MESSAGE_GET_LATENCY messages truncate negative
latencies because they do not make sense from a physical point of view. In fact,
the values are truncated twice, once in the message handler and a second time in
the pa_{source,sink}_get_latency_within_thread() call itself.
This leads to two problems for the latency controller within module-loopback:
- Truncating leads to discontinuities in the latency reports which then trigger
unwanted end to end latency corrections.
- If a large negative port latency offsets is set, the reported latency is always 0,
making it impossible to control the end to end latency at all.
This patch is a pre-condition for solving these problems.
It adds a new flag to pa_{sink,source}_get_latency_within_thread() to allow
negative return values. Truncating is also removed in all implementations of the
PA_{SINK,SOURCE}_MESSAGE_GET_LATENCY message handlers. The allow_negative flag
is set to false for all calls of pa_{sink,source}_get_latency_within_thread()
except when used within PA_{SINK,SOURCE}_MESSAGE_GET_LATENCY. This means that the
original behavior is not altered in most cases. Only if a positive latency offset
is set and the message returns a negative value, the reported latency is smaller
because the values are not truncated twice.
Additionally let PA_SOURCE_MESSAGE_GET_LATENCY return -pa_sink_get_latency_within_thread()
for monitor sources because the source gets the data before it is played.
2017-04-17 19:50:10 +02:00
|
|
|
s->current_monitor_latency = o->source->monitor_of ? pa_sink_get_latency_within_thread(o->source->monitor_of, false) : 0;
|
|
|
|
|
s->current_source_latency = pa_source_get_latency_within_thread(o->source, false);
|
2009-04-05 02:59:02 +02:00
|
|
|
s->on_the_fly_snapshot = pa_atomic_load(&s->on_the_fly);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return pa_source_output_process_msg(_o, code, userdata, offset, chunk);
|
|
|
|
|
}
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
/* Called from thread context */
|
2006-01-11 01:17:39 +00:00
|
|
|
static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
|
2007-10-28 19:13:50 +00:00
|
|
|
record_stream *s;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_source_output_assert_ref(o);
|
|
|
|
|
s = RECORD_STREAM(o->userdata);
|
|
|
|
|
record_stream_assert_ref(s);
|
|
|
|
|
pa_assert(chunk);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2009-04-05 02:59:02 +02:00
|
|
|
pa_atomic_add(&s->on_the_fly, chunk->length);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), RECORD_STREAM_MESSAGE_POST_DATA, NULL, 0, chunk, NULL);
|
2004-07-10 16:50:22 +00:00
|
|
|
}
|
|
|
|
|
|
2006-01-11 01:17:39 +00:00
|
|
|
static void source_output_kill_cb(pa_source_output *o) {
|
2007-10-28 19:13:50 +00:00
|
|
|
record_stream *s;
|
|
|
|
|
|
|
|
|
|
pa_source_output_assert_ref(o);
|
|
|
|
|
s = RECORD_STREAM(o->userdata);
|
|
|
|
|
record_stream_assert_ref(s);
|
|
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
record_stream_send_killed(s);
|
2007-10-28 19:13:50 +00:00
|
|
|
record_stream_unlink(s);
|
2004-07-10 16:50:22 +00:00
|
|
|
}
|
|
|
|
|
|
2006-01-11 01:17:39 +00:00
|
|
|
static pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
|
2007-10-28 19:13:50 +00:00
|
|
|
record_stream *s;
|
|
|
|
|
|
|
|
|
|
pa_source_output_assert_ref(o);
|
|
|
|
|
s = RECORD_STREAM(o->userdata);
|
|
|
|
|
record_stream_assert_ref(s);
|
2004-09-16 00:05:56 +00:00
|
|
|
|
2006-08-18 21:38:40 +00:00
|
|
|
/*pa_log("get_latency: %u", pa_memblockq_get_length(s->memblockq));*/
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-09-16 00:05:56 +00:00
|
|
|
return pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &o->sample_spec);
|
|
|
|
|
}
|
|
|
|
|
|
2009-02-12 03:18:05 +01:00
|
|
|
/* Called from main context */
|
|
|
|
|
static void source_output_send_event_cb(pa_source_output *o, const char *event, pa_proplist *pl) {
|
|
|
|
|
record_stream *s;
|
|
|
|
|
pa_tagstruct *t;
|
|
|
|
|
|
|
|
|
|
pa_source_output_assert_ref(o);
|
|
|
|
|
s = RECORD_STREAM(o->userdata);
|
|
|
|
|
record_stream_assert_ref(s);
|
|
|
|
|
|
|
|
|
|
if (s->connection->version < 15)
|
|
|
|
|
return;
|
|
|
|
|
|
2014-10-23 16:09:45 +02:00
|
|
|
t = pa_tagstruct_new();
|
2009-02-12 03:18:05 +01:00
|
|
|
pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_EVENT);
|
|
|
|
|
pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
|
|
|
|
|
pa_tagstruct_putu32(t, s->index);
|
|
|
|
|
pa_tagstruct_puts(t, event);
|
|
|
|
|
pa_tagstruct_put_proplist(t, pl);
|
|
|
|
|
pa_pstream_send_tagstruct(s->connection->pstream, t);
|
|
|
|
|
}
|
|
|
|
|
|
2007-11-21 01:30:40 +00:00
|
|
|
/* Called from main context */
|
2019-02-12 20:21:01 +01:00
|
|
|
static void source_output_suspend_cb(pa_source_output *o, pa_source_state_t old_state, pa_suspend_cause_t old_suspend_cause) {
|
2007-11-21 01:30:40 +00:00
|
|
|
record_stream *s;
|
|
|
|
|
pa_tagstruct *t;
|
2019-02-12 20:21:01 +01:00
|
|
|
bool suspend;
|
2007-11-21 01:30:40 +00:00
|
|
|
|
|
|
|
|
pa_source_output_assert_ref(o);
|
2019-02-12 20:21:01 +01:00
|
|
|
|
|
|
|
|
/* State has not changed, nothing to do */
|
|
|
|
|
if (old_state == o->source->state)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
suspend = (o->source->state == PA_SOURCE_SUSPENDED);
|
|
|
|
|
|
2007-11-21 01:30:40 +00:00
|
|
|
s = RECORD_STREAM(o->userdata);
|
|
|
|
|
record_stream_assert_ref(s);
|
|
|
|
|
|
2008-01-04 14:59:09 +00:00
|
|
|
if (s->connection->version < 12)
|
|
|
|
|
return;
|
|
|
|
|
|
2014-10-23 16:09:45 +02:00
|
|
|
t = pa_tagstruct_new();
|
2007-11-21 01:30:40 +00:00
|
|
|
pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_SUSPENDED);
|
|
|
|
|
pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
|
|
|
|
|
pa_tagstruct_putu32(t, s->index);
|
|
|
|
|
pa_tagstruct_put_boolean(t, suspend);
|
|
|
|
|
pa_pstream_send_tagstruct(s->connection->pstream, t);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Called from main context */
|
2009-04-01 03:04:39 +02:00
|
|
|
static void source_output_moving_cb(pa_source_output *o, pa_source *dest) {
|
2007-11-21 01:30:40 +00:00
|
|
|
record_stream *s;
|
|
|
|
|
pa_tagstruct *t;
|
|
|
|
|
|
|
|
|
|
pa_source_output_assert_ref(o);
|
|
|
|
|
s = RECORD_STREAM(o->userdata);
|
|
|
|
|
record_stream_assert_ref(s);
|
|
|
|
|
|
2009-08-15 00:05:17 +02:00
|
|
|
if (!dest)
|
|
|
|
|
return;
|
|
|
|
|
|
2009-03-30 18:46:12 +02:00
|
|
|
fix_record_buffer_attr_pre(s);
|
|
|
|
|
pa_memblockq_set_maxlength(s->memblockq, s->buffer_attr.maxlength);
|
|
|
|
|
pa_memblockq_get_attr(s->memblockq, &s->buffer_attr);
|
|
|
|
|
fix_record_buffer_attr_post(s);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2008-01-04 14:59:09 +00:00
|
|
|
if (s->connection->version < 12)
|
|
|
|
|
return;
|
|
|
|
|
|
2014-10-23 16:09:45 +02:00
|
|
|
t = pa_tagstruct_new();
|
2007-11-21 01:30:40 +00:00
|
|
|
pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_MOVED);
|
|
|
|
|
pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
|
|
|
|
|
pa_tagstruct_putu32(t, s->index);
|
2009-04-01 03:04:39 +02:00
|
|
|
pa_tagstruct_putu32(t, dest->index);
|
|
|
|
|
pa_tagstruct_puts(t, dest->name);
|
2018-06-26 16:25:58 +03:00
|
|
|
pa_tagstruct_put_boolean(t, dest->state == PA_SOURCE_SUSPENDED);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
|
|
|
|
if (s->connection->version >= 13) {
|
2009-03-30 18:46:12 +02:00
|
|
|
pa_tagstruct_putu32(t, s->buffer_attr.maxlength);
|
|
|
|
|
pa_tagstruct_putu32(t, s->buffer_attr.fragsize);
|
2009-04-05 02:59:02 +02:00
|
|
|
pa_tagstruct_put_usec(t, s->configured_source_latency);
|
2008-05-15 23:34:41 +00:00
|
|
|
}
|
|
|
|
|
|
2007-11-21 01:30:40 +00:00
|
|
|
pa_pstream_send_tagstruct(s->connection->pstream, t);
|
|
|
|
|
}
|
|
|
|
|
|
2004-06-23 23:17:30 +00:00
|
|
|
/*** pdispatch callbacks ***/
|
|
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
static void protocol_error(pa_native_connection *c) {
|
2006-08-18 21:38:40 +00:00
|
|
|
pa_log("protocol error, kicking client");
|
2008-08-04 18:58:29 +02:00
|
|
|
native_connection_unlink(c);
|
2004-07-07 00:22:46 +00:00
|
|
|
}
|
|
|
|
|
|
2006-02-22 18:54:21 +00:00
|
|
|
#define CHECK_VALIDITY(pstream, expression, tag, error) do { \
|
|
|
|
|
if (!(expression)) { \
|
|
|
|
|
pa_pstream_send_error((pstream), (tag), (error)); \
|
|
|
|
|
return; \
|
|
|
|
|
} \
|
|
|
|
|
} while(0);
|
|
|
|
|
|
2011-05-16 09:26:32 +01:00
|
|
|
#define CHECK_VALIDITY_GOTO(pstream, expression, tag, error, label) do { \
|
|
|
|
|
if (!(expression)) { \
|
|
|
|
|
pa_pstream_send_error((pstream), (tag), (error)); \
|
|
|
|
|
goto label; \
|
|
|
|
|
} \
|
|
|
|
|
} while(0);
|
|
|
|
|
|
2006-02-22 18:54:21 +00:00
|
|
|
static pa_tagstruct *reply_new(uint32_t tag) {
|
|
|
|
|
pa_tagstruct *reply;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2014-10-23 16:09:45 +02:00
|
|
|
reply = pa_tagstruct_new();
|
2006-02-22 18:54:21 +00:00
|
|
|
pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
|
|
|
|
|
pa_tagstruct_putu32(reply, tag);
|
|
|
|
|
return reply;
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-09 16:20:29 +02:00
|
|
|
static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
2007-10-28 19:13:50 +00:00
|
|
|
playback_stream *s;
|
2011-07-12 14:46:21 -07:00
|
|
|
uint32_t sink_index, syncid, missing = 0;
|
2009-03-30 18:46:12 +02:00
|
|
|
pa_buffer_attr attr;
|
2008-05-15 23:34:41 +00:00
|
|
|
const char *name = NULL, *sink_name;
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_sample_spec ss;
|
2006-01-27 16:25:31 +00:00
|
|
|
pa_channel_map map;
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_tagstruct *reply;
|
2006-08-19 23:06:45 +00:00
|
|
|
pa_sink *sink = NULL;
|
2006-01-27 16:25:31 +00:00
|
|
|
pa_cvolume volume;
|
2013-06-27 19:28:09 +02:00
|
|
|
bool
|
|
|
|
|
corked = false,
|
|
|
|
|
no_remap = false,
|
|
|
|
|
no_remix = false,
|
|
|
|
|
fix_format = false,
|
|
|
|
|
fix_rate = false,
|
|
|
|
|
fix_channels = false,
|
|
|
|
|
no_move = false,
|
|
|
|
|
variable_rate = false,
|
|
|
|
|
muted = false,
|
|
|
|
|
adjust_latency = false,
|
|
|
|
|
early_requests = false,
|
|
|
|
|
dont_inhibit_auto_suspend = false,
|
|
|
|
|
volume_set = true,
|
|
|
|
|
muted_set = false,
|
|
|
|
|
fail_on_suspend = false,
|
|
|
|
|
relative_volume = false,
|
|
|
|
|
passthrough = false;
|
2010-07-16 16:46:28 -05:00
|
|
|
|
2007-11-21 01:30:40 +00:00
|
|
|
pa_sink_input_flags_t flags = 0;
|
2011-02-28 13:23:23 +05:30
|
|
|
pa_proplist *p = NULL;
|
2009-02-03 03:14:20 +01:00
|
|
|
int ret = PA_ERR_INVALID;
|
2011-02-28 13:23:23 +05:30
|
|
|
uint8_t n_formats = 0;
|
|
|
|
|
pa_format_info *format;
|
|
|
|
|
pa_idxset *formats = NULL;
|
|
|
|
|
uint32_t i;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_assert_ref(c);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(t);
|
2009-03-30 18:46:12 +02:00
|
|
|
memset(&attr, 0, sizeof(attr));
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if ((c->version < 13 && (pa_tagstruct_gets(t, &name) < 0 || !name)) ||
|
|
|
|
|
pa_tagstruct_get(
|
|
|
|
|
t,
|
|
|
|
|
PA_TAG_SAMPLE_SPEC, &ss,
|
|
|
|
|
PA_TAG_CHANNEL_MAP, &map,
|
|
|
|
|
PA_TAG_U32, &sink_index,
|
|
|
|
|
PA_TAG_STRING, &sink_name,
|
2009-03-30 18:46:12 +02:00
|
|
|
PA_TAG_U32, &attr.maxlength,
|
2008-05-15 23:34:41 +00:00
|
|
|
PA_TAG_BOOLEAN, &corked,
|
2009-03-30 18:46:12 +02:00
|
|
|
PA_TAG_U32, &attr.tlength,
|
|
|
|
|
PA_TAG_U32, &attr.prebuf,
|
|
|
|
|
PA_TAG_U32, &attr.minreq,
|
2008-05-15 23:34:41 +00:00
|
|
|
PA_TAG_U32, &syncid,
|
|
|
|
|
PA_TAG_CVOLUME, &volume,
|
|
|
|
|
PA_TAG_INVALID) < 0) {
|
|
|
|
|
|
2007-11-21 01:30:40 +00:00
|
|
|
protocol_error(c);
|
2011-05-16 09:26:32 +01:00
|
|
|
goto finish;
|
2007-11-21 01:30:40 +00:00
|
|
|
}
|
|
|
|
|
|
2011-05-16 09:26:32 +01:00
|
|
|
CHECK_VALIDITY_GOTO(c->pstream, c->authorized, tag, PA_ERR_ACCESS, finish);
|
|
|
|
|
CHECK_VALIDITY_GOTO(c->pstream, !sink_name || pa_namereg_is_valid_name_or_wildcard(sink_name, PA_NAMEREG_SINK), tag, PA_ERR_INVALID, finish);
|
|
|
|
|
CHECK_VALIDITY_GOTO(c->pstream, sink_index == PA_INVALID_INDEX || !sink_name, tag, PA_ERR_INVALID, finish);
|
|
|
|
|
CHECK_VALIDITY_GOTO(c->pstream, !sink_name || sink_index == PA_INVALID_INDEX, tag, PA_ERR_INVALID, finish);
|
|
|
|
|
CHECK_VALIDITY_GOTO(c->pstream, pa_cvolume_valid(&volume), tag, PA_ERR_INVALID, finish);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
|
|
|
|
p = pa_proplist_new();
|
|
|
|
|
|
|
|
|
|
if (name)
|
|
|
|
|
pa_proplist_sets(p, PA_PROP_MEDIA_NAME, name);
|
|
|
|
|
|
2011-03-12 19:45:02 +01:00
|
|
|
if (c->version >= 12) {
|
2007-11-21 01:30:40 +00:00
|
|
|
/* Since 0.9.8 the user can ask for a couple of additional flags */
|
|
|
|
|
|
|
|
|
|
if (pa_tagstruct_get_boolean(t, &no_remap) < 0 ||
|
|
|
|
|
pa_tagstruct_get_boolean(t, &no_remix) < 0 ||
|
|
|
|
|
pa_tagstruct_get_boolean(t, &fix_format) < 0 ||
|
|
|
|
|
pa_tagstruct_get_boolean(t, &fix_rate) < 0 ||
|
|
|
|
|
pa_tagstruct_get_boolean(t, &fix_channels) < 0 ||
|
|
|
|
|
pa_tagstruct_get_boolean(t, &no_move) < 0 ||
|
|
|
|
|
pa_tagstruct_get_boolean(t, &variable_rate) < 0) {
|
2008-05-15 23:34:41 +00:00
|
|
|
|
|
|
|
|
protocol_error(c);
|
2011-05-16 09:26:32 +01:00
|
|
|
goto finish;
|
2008-05-15 23:34:41 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (c->version >= 13) {
|
|
|
|
|
|
|
|
|
|
if (pa_tagstruct_get_boolean(t, &muted) < 0 ||
|
|
|
|
|
pa_tagstruct_get_boolean(t, &adjust_latency) < 0 ||
|
|
|
|
|
pa_tagstruct_get_proplist(t, p) < 0) {
|
2011-02-28 13:23:23 +05:30
|
|
|
|
2007-11-21 01:30:40 +00:00
|
|
|
protocol_error(c);
|
2011-05-16 09:26:32 +01:00
|
|
|
goto finish;
|
2007-11-21 01:30:40 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-18 17:49:18 +02:00
|
|
|
if (c->version >= 14) {
|
|
|
|
|
|
2008-09-03 18:31:46 +02:00
|
|
|
if (pa_tagstruct_get_boolean(t, &volume_set) < 0 ||
|
|
|
|
|
pa_tagstruct_get_boolean(t, &early_requests) < 0) {
|
2011-02-28 13:23:23 +05:30
|
|
|
|
2008-08-18 17:49:18 +02:00
|
|
|
protocol_error(c);
|
2011-05-16 09:26:32 +01:00
|
|
|
goto finish;
|
2008-08-18 17:49:18 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-10-26 19:32:04 +01:00
|
|
|
if (c->version >= 15) {
|
|
|
|
|
|
|
|
|
|
if (pa_tagstruct_get_boolean(t, &muted_set) < 0 ||
|
2009-02-03 02:23:46 +01:00
|
|
|
pa_tagstruct_get_boolean(t, &dont_inhibit_auto_suspend) < 0 ||
|
|
|
|
|
pa_tagstruct_get_boolean(t, &fail_on_suspend) < 0) {
|
2011-02-28 13:23:23 +05:30
|
|
|
|
2008-10-26 19:32:04 +01:00
|
|
|
protocol_error(c);
|
2011-05-16 09:26:32 +01:00
|
|
|
goto finish;
|
2008-10-26 19:32:04 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-29 00:46:22 +01:00
|
|
|
if (c->version >= 17) {
|
|
|
|
|
|
|
|
|
|
if (pa_tagstruct_get_boolean(t, &relative_volume) < 0) {
|
2011-02-28 13:23:23 +05:30
|
|
|
|
2009-10-29 00:46:22 +01:00
|
|
|
protocol_error(c);
|
2011-05-16 09:26:32 +01:00
|
|
|
goto finish;
|
2009-10-29 00:46:22 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-16 16:46:28 -05:00
|
|
|
if (c->version >= 18) {
|
|
|
|
|
|
|
|
|
|
if (pa_tagstruct_get_boolean(t, &passthrough) < 0 ) {
|
|
|
|
|
protocol_error(c);
|
2011-05-16 09:26:32 +01:00
|
|
|
goto finish;
|
2011-02-28 13:23:23 +05:30
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (c->version >= 21) {
|
|
|
|
|
|
|
|
|
|
if (pa_tagstruct_getu8(t, &n_formats) < 0) {
|
|
|
|
|
protocol_error(c);
|
2011-05-16 09:26:32 +01:00
|
|
|
goto finish;
|
2011-02-28 13:23:23 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (n_formats)
|
|
|
|
|
formats = pa_idxset_new(NULL, NULL);
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < n_formats; i++) {
|
|
|
|
|
format = pa_format_info_new();
|
|
|
|
|
if (pa_tagstruct_get_format_info(t, format) < 0) {
|
|
|
|
|
protocol_error(c);
|
2011-05-16 09:26:32 +01:00
|
|
|
goto finish;
|
2011-02-28 13:23:23 +05:30
|
|
|
}
|
|
|
|
|
pa_idxset_put(formats, format, NULL);
|
2010-07-16 16:46:28 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-02 12:54:02 +05:30
|
|
|
if (n_formats == 0) {
|
2011-05-16 09:26:32 +01:00
|
|
|
CHECK_VALIDITY_GOTO(c->pstream, pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID, finish);
|
|
|
|
|
CHECK_VALIDITY_GOTO(c->pstream, map.channels == ss.channels && volume.channels == ss.channels, tag, PA_ERR_INVALID, finish);
|
|
|
|
|
CHECK_VALIDITY_GOTO(c->pstream, pa_channel_map_valid(&map), tag, PA_ERR_INVALID, finish);
|
2011-03-02 12:54:02 +05:30
|
|
|
} else {
|
|
|
|
|
PA_IDXSET_FOREACH(format, formats, i) {
|
2011-05-16 09:26:32 +01:00
|
|
|
CHECK_VALIDITY_GOTO(c->pstream, pa_format_info_valid(format), tag, PA_ERR_INVALID, finish);
|
2011-03-02 12:54:02 +05:30
|
|
|
}
|
|
|
|
|
}
|
2011-02-28 13:23:23 +05:30
|
|
|
|
2007-11-21 01:30:40 +00:00
|
|
|
if (!pa_tagstruct_eof(t)) {
|
2004-07-07 00:22:46 +00:00
|
|
|
protocol_error(c);
|
2011-05-16 09:26:32 +01:00
|
|
|
goto finish;
|
2004-07-07 00:22:46 +00:00
|
|
|
}
|
2004-06-20 01:12:13 +00:00
|
|
|
|
2006-08-19 23:06:45 +00:00
|
|
|
if (sink_index != PA_INVALID_INDEX) {
|
2008-05-15 23:34:41 +00:00
|
|
|
|
|
|
|
|
if (!(sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index))) {
|
|
|
|
|
pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
|
2011-05-16 09:26:32 +01:00
|
|
|
goto finish;
|
2008-05-15 23:34:41 +00:00
|
|
|
}
|
|
|
|
|
|
2006-08-19 23:06:45 +00:00
|
|
|
} else if (sink_name) {
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2009-01-15 20:07:13 +01:00
|
|
|
if (!(sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK))) {
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
|
2011-05-16 09:26:32 +01:00
|
|
|
goto finish;
|
2008-05-15 23:34:41 +00:00
|
|
|
}
|
2006-08-19 23:06:45 +00:00
|
|
|
}
|
2006-02-22 18:54:21 +00:00
|
|
|
|
2007-11-21 01:30:40 +00:00
|
|
|
flags =
|
2011-03-12 19:45:02 +01:00
|
|
|
(corked ? PA_SINK_INPUT_START_CORKED : 0) |
|
|
|
|
|
(no_remap ? PA_SINK_INPUT_NO_REMAP : 0) |
|
|
|
|
|
(no_remix ? PA_SINK_INPUT_NO_REMIX : 0) |
|
|
|
|
|
(fix_format ? PA_SINK_INPUT_FIX_FORMAT : 0) |
|
|
|
|
|
(fix_rate ? PA_SINK_INPUT_FIX_RATE : 0) |
|
|
|
|
|
(fix_channels ? PA_SINK_INPUT_FIX_CHANNELS : 0) |
|
|
|
|
|
(no_move ? PA_SINK_INPUT_DONT_MOVE : 0) |
|
|
|
|
|
(variable_rate ? PA_SINK_INPUT_VARIABLE_RATE : 0) |
|
2009-02-03 02:23:46 +01:00
|
|
|
(dont_inhibit_auto_suspend ? PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND : 0) |
|
2010-07-16 16:46:28 -05:00
|
|
|
(fail_on_suspend ? PA_SINK_INPUT_NO_CREATE_ON_SUSPEND|PA_SINK_INPUT_KILL_ON_SUSPEND : 0) |
|
|
|
|
|
(passthrough ? PA_SINK_INPUT_PASSTHROUGH : 0);
|
2008-10-26 19:32:04 +01:00
|
|
|
|
2011-08-24 18:24:46 +02:00
|
|
|
/* Only since protocol version 15 there's a separate muted_set
|
2008-10-26 19:32:04 +01:00
|
|
|
* flag. For older versions we synthesize it here */
|
|
|
|
|
muted_set = muted_set || muted;
|
2007-11-21 01:30:40 +00:00
|
|
|
|
2011-05-17 21:56:10 +01:00
|
|
|
s = playback_stream_new(c, sink, &ss, &map, formats, &attr, volume_set ? &volume : NULL, muted, muted_set, flags, p, adjust_latency, early_requests, relative_volume, syncid, &missing, &ret);
|
2011-06-02 19:35:09 +05:30
|
|
|
/* We no longer own the formats idxset */
|
|
|
|
|
formats = NULL;
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2011-05-16 09:26:32 +01:00
|
|
|
CHECK_VALIDITY_GOTO(c->pstream, s, tag, ret, finish);
|
2004-09-26 17:02:26 +00:00
|
|
|
|
2006-02-22 18:54:21 +00:00
|
|
|
reply = reply_new(tag);
|
2004-07-03 23:35:12 +00:00
|
|
|
pa_tagstruct_putu32(reply, s->index);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(s->sink_input);
|
2004-07-03 23:35:12 +00:00
|
|
|
pa_tagstruct_putu32(reply, s->sink_input->index);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_tagstruct_putu32(reply, missing);
|
|
|
|
|
|
2011-11-10 12:43:09 +05:30
|
|
|
#ifdef PROTOCOL_NATIVE_DEBUG
|
|
|
|
|
pa_log("initial request is %u", missing);
|
|
|
|
|
#endif
|
2006-05-25 23:20:28 +00:00
|
|
|
|
|
|
|
|
if (c->version >= 9) {
|
2007-11-21 01:30:40 +00:00
|
|
|
/* Since 0.9.0 we support sending the buffer metrics back to the client */
|
2006-05-25 23:20:28 +00:00
|
|
|
|
2009-03-30 18:46:12 +02:00
|
|
|
pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.maxlength);
|
|
|
|
|
pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.tlength);
|
|
|
|
|
pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.prebuf);
|
|
|
|
|
pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.minreq);
|
2006-05-25 23:20:28 +00:00
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-11-21 01:30:40 +00:00
|
|
|
if (c->version >= 12) {
|
|
|
|
|
/* Since 0.9.8 we support sending the chosen sample
|
|
|
|
|
* spec/channel map/device/suspend status back to the
|
|
|
|
|
* client */
|
|
|
|
|
|
|
|
|
|
pa_tagstruct_put_sample_spec(reply, &ss);
|
|
|
|
|
pa_tagstruct_put_channel_map(reply, &map);
|
|
|
|
|
|
|
|
|
|
pa_tagstruct_putu32(reply, s->sink_input->sink->index);
|
|
|
|
|
pa_tagstruct_puts(reply, s->sink_input->sink->name);
|
|
|
|
|
|
2018-06-26 16:25:58 +03:00
|
|
|
pa_tagstruct_put_boolean(reply, s->sink_input->sink->state == PA_SINK_SUSPENDED);
|
2007-11-21 01:30:40 +00:00
|
|
|
}
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (c->version >= 13)
|
2009-04-05 02:59:02 +02:00
|
|
|
pa_tagstruct_put_usec(reply, s->configured_sink_latency);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2011-02-28 13:23:23 +05:30
|
|
|
if (c->version >= 21) {
|
|
|
|
|
/* Send back the format we negotiated */
|
|
|
|
|
if (s->sink_input->format)
|
|
|
|
|
pa_tagstruct_put_format_info(reply, s->sink_input->format);
|
|
|
|
|
else {
|
|
|
|
|
pa_format_info *f = pa_format_info_new();
|
|
|
|
|
pa_tagstruct_put_format_info(reply, f);
|
|
|
|
|
pa_format_info_free(f);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2004-07-03 23:35:12 +00:00
|
|
|
pa_pstream_send_tagstruct(c->pstream, reply);
|
2011-02-28 13:23:23 +05:30
|
|
|
|
2011-05-16 09:26:32 +01:00
|
|
|
finish:
|
2011-02-28 13:23:23 +05:30
|
|
|
if (p)
|
|
|
|
|
pa_proplist_free(p);
|
|
|
|
|
if (formats)
|
2013-02-12 21:36:55 +02:00
|
|
|
pa_idxset_free(formats, (pa_free_cb_t) pa_format_info_free);
|
2004-06-20 01:12:13 +00:00
|
|
|
}
|
|
|
|
|
|
2008-08-09 16:20:29 +02:00
|
|
|
static void command_delete_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
2004-06-20 01:12:13 +00:00
|
|
|
uint32_t channel;
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_assert_ref(c);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(t);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-07-03 23:35:12 +00:00
|
|
|
if (pa_tagstruct_getu32(t, &channel) < 0 ||
|
2004-07-07 00:22:46 +00:00
|
|
|
!pa_tagstruct_eof(t)) {
|
|
|
|
|
protocol_error(c);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2004-06-20 01:12:13 +00:00
|
|
|
|
2006-02-22 18:54:21 +00:00
|
|
|
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
|
2004-06-20 01:12:13 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
switch (command) {
|
|
|
|
|
|
|
|
|
|
case PA_COMMAND_DELETE_PLAYBACK_STREAM: {
|
|
|
|
|
playback_stream *s;
|
|
|
|
|
if (!(s = pa_idxset_get_by_index(c->output_streams, channel)) || !playback_stream_isinstance(s)) {
|
2020-03-05 09:11:09 +02:00
|
|
|
pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
|
2007-10-28 19:13:50 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
playback_stream_unlink(s);
|
|
|
|
|
break;
|
2004-08-03 19:26:56 +00:00
|
|
|
}
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
case PA_COMMAND_DELETE_RECORD_STREAM: {
|
|
|
|
|
record_stream *s;
|
|
|
|
|
if (!(s = pa_idxset_get_by_index(c->record_streams, channel))) {
|
2020-03-05 09:11:09 +02:00
|
|
|
pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
|
2007-10-28 19:13:50 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
record_stream_unlink(s);
|
|
|
|
|
break;
|
2004-08-03 19:26:56 +00:00
|
|
|
}
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
case PA_COMMAND_DELETE_UPLOAD_STREAM: {
|
|
|
|
|
upload_stream *s;
|
|
|
|
|
|
|
|
|
|
if (!(s = pa_idxset_get_by_index(c->output_streams, channel)) || !upload_stream_isinstance(s)) {
|
2020-03-05 09:11:09 +02:00
|
|
|
pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
|
2007-10-28 19:13:50 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
upload_stream_unlink(s);
|
|
|
|
|
break;
|
2004-08-03 19:26:56 +00:00
|
|
|
}
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
default:
|
|
|
|
|
pa_assert_not_reached();
|
2004-08-03 19:26:56 +00:00
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-07-10 16:50:22 +00:00
|
|
|
pa_pstream_send_simple_ack(c->pstream, tag);
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-09 16:20:29 +02:00
|
|
|
static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
2007-10-28 19:13:50 +00:00
|
|
|
record_stream *s;
|
2009-03-30 18:46:12 +02:00
|
|
|
pa_buffer_attr attr;
|
2004-07-10 16:50:22 +00:00
|
|
|
uint32_t source_index;
|
2008-06-13 21:56:19 +00:00
|
|
|
const char *name = NULL, *source_name;
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_sample_spec ss;
|
2006-01-27 16:25:31 +00:00
|
|
|
pa_channel_map map;
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_tagstruct *reply;
|
2006-08-19 23:06:45 +00:00
|
|
|
pa_source *source = NULL;
|
2011-05-17 22:31:10 +01:00
|
|
|
pa_cvolume volume;
|
2013-06-27 19:28:09 +02:00
|
|
|
bool
|
|
|
|
|
corked = false,
|
|
|
|
|
no_remap = false,
|
|
|
|
|
no_remix = false,
|
|
|
|
|
fix_format = false,
|
|
|
|
|
fix_rate = false,
|
|
|
|
|
fix_channels = false,
|
|
|
|
|
no_move = false,
|
|
|
|
|
variable_rate = false,
|
|
|
|
|
muted = false,
|
|
|
|
|
adjust_latency = false,
|
|
|
|
|
peak_detect = false,
|
|
|
|
|
early_requests = false,
|
|
|
|
|
dont_inhibit_auto_suspend = false,
|
|
|
|
|
volume_set = false,
|
|
|
|
|
muted_set = false,
|
|
|
|
|
fail_on_suspend = false,
|
|
|
|
|
relative_volume = false,
|
|
|
|
|
passthrough = false;
|
2011-05-17 21:56:10 +01:00
|
|
|
|
2007-11-21 01:30:40 +00:00
|
|
|
pa_source_output_flags_t flags = 0;
|
2011-05-17 21:56:10 +01:00
|
|
|
pa_proplist *p = NULL;
|
2008-06-13 21:56:19 +00:00
|
|
|
uint32_t direct_on_input_idx = PA_INVALID_INDEX;
|
|
|
|
|
pa_sink_input *direct_on_input = NULL;
|
2009-02-03 03:14:20 +01:00
|
|
|
int ret = PA_ERR_INVALID;
|
2011-05-17 21:56:10 +01:00
|
|
|
uint8_t n_formats = 0;
|
|
|
|
|
pa_format_info *format;
|
|
|
|
|
pa_idxset *formats = NULL;
|
|
|
|
|
uint32_t i;
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_assert_ref(c);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(t);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2009-03-30 18:46:12 +02:00
|
|
|
memset(&attr, 0, sizeof(attr));
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if ((c->version < 13 && (pa_tagstruct_gets(t, &name) < 0 || !name)) ||
|
2004-07-10 16:50:22 +00:00
|
|
|
pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
|
2006-01-27 16:25:31 +00:00
|
|
|
pa_tagstruct_get_channel_map(t, &map) < 0 ||
|
2004-07-10 16:50:22 +00:00
|
|
|
pa_tagstruct_getu32(t, &source_index) < 0 ||
|
2004-08-03 19:26:56 +00:00
|
|
|
pa_tagstruct_gets(t, &source_name) < 0 ||
|
2009-03-30 18:46:12 +02:00
|
|
|
pa_tagstruct_getu32(t, &attr.maxlength) < 0 ||
|
2004-09-29 17:38:45 +00:00
|
|
|
pa_tagstruct_get_boolean(t, &corked) < 0 ||
|
2009-03-30 18:46:12 +02:00
|
|
|
pa_tagstruct_getu32(t, &attr.fragsize) < 0) {
|
2011-05-17 21:56:10 +01:00
|
|
|
|
2004-07-10 16:50:22 +00:00
|
|
|
protocol_error(c);
|
2011-05-17 21:56:10 +01:00
|
|
|
goto finish;
|
2004-07-10 16:50:22 +00:00
|
|
|
}
|
|
|
|
|
|
2011-05-17 21:56:10 +01:00
|
|
|
CHECK_VALIDITY_GOTO(c->pstream, c->authorized, tag, PA_ERR_ACCESS, finish);
|
|
|
|
|
CHECK_VALIDITY_GOTO(c->pstream, !source_name || pa_namereg_is_valid_name_or_wildcard(source_name, PA_NAMEREG_SOURCE), tag, PA_ERR_INVALID, finish);
|
|
|
|
|
CHECK_VALIDITY_GOTO(c->pstream, source_index == PA_INVALID_INDEX || !source_name, tag, PA_ERR_INVALID, finish);
|
|
|
|
|
CHECK_VALIDITY_GOTO(c->pstream, !source_name || source_index == PA_INVALID_INDEX, tag, PA_ERR_INVALID, finish);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
|
|
|
|
p = pa_proplist_new();
|
|
|
|
|
|
|
|
|
|
if (name)
|
|
|
|
|
pa_proplist_sets(p, PA_PROP_MEDIA_NAME, name);
|
|
|
|
|
|
2011-03-12 19:45:02 +01:00
|
|
|
if (c->version >= 12) {
|
2007-11-21 01:30:40 +00:00
|
|
|
/* Since 0.9.8 the user can ask for a couple of additional flags */
|
|
|
|
|
|
|
|
|
|
if (pa_tagstruct_get_boolean(t, &no_remap) < 0 ||
|
|
|
|
|
pa_tagstruct_get_boolean(t, &no_remix) < 0 ||
|
|
|
|
|
pa_tagstruct_get_boolean(t, &fix_format) < 0 ||
|
|
|
|
|
pa_tagstruct_get_boolean(t, &fix_rate) < 0 ||
|
|
|
|
|
pa_tagstruct_get_boolean(t, &fix_channels) < 0 ||
|
|
|
|
|
pa_tagstruct_get_boolean(t, &no_move) < 0 ||
|
|
|
|
|
pa_tagstruct_get_boolean(t, &variable_rate) < 0) {
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2007-11-21 01:30:40 +00:00
|
|
|
protocol_error(c);
|
2011-05-17 21:56:10 +01:00
|
|
|
goto finish;
|
2008-05-15 23:34:41 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (c->version >= 13) {
|
|
|
|
|
|
|
|
|
|
if (pa_tagstruct_get_boolean(t, &peak_detect) < 0 ||
|
|
|
|
|
pa_tagstruct_get_boolean(t, &adjust_latency) < 0 ||
|
2008-06-13 21:56:19 +00:00
|
|
|
pa_tagstruct_get_proplist(t, p) < 0 ||
|
|
|
|
|
pa_tagstruct_getu32(t, &direct_on_input_idx) < 0) {
|
2011-05-17 21:56:10 +01:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
protocol_error(c);
|
2011-05-17 21:56:10 +01:00
|
|
|
goto finish;
|
2007-11-21 01:30:40 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-09-03 18:31:46 +02:00
|
|
|
if (c->version >= 14) {
|
|
|
|
|
|
|
|
|
|
if (pa_tagstruct_get_boolean(t, &early_requests) < 0) {
|
|
|
|
|
protocol_error(c);
|
2011-05-17 21:56:10 +01:00
|
|
|
goto finish;
|
2008-09-03 18:31:46 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-10-26 19:32:04 +01:00
|
|
|
if (c->version >= 15) {
|
|
|
|
|
|
2009-02-03 02:23:46 +01:00
|
|
|
if (pa_tagstruct_get_boolean(t, &dont_inhibit_auto_suspend) < 0 ||
|
|
|
|
|
pa_tagstruct_get_boolean(t, &fail_on_suspend) < 0) {
|
2011-05-17 21:56:10 +01:00
|
|
|
|
2008-10-26 19:32:04 +01:00
|
|
|
protocol_error(c);
|
2011-05-17 21:56:10 +01:00
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (c->version >= 22) {
|
2011-07-09 18:27:45 -07:00
|
|
|
/* For newer client versions (with per-source-output volumes), we try
|
|
|
|
|
* to make the behaviour for playback and record streams the same. */
|
2013-06-27 19:28:09 +02:00
|
|
|
volume_set = true;
|
2011-05-17 21:56:10 +01:00
|
|
|
|
|
|
|
|
if (pa_tagstruct_getu8(t, &n_formats) < 0) {
|
|
|
|
|
protocol_error(c);
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (n_formats)
|
|
|
|
|
formats = pa_idxset_new(NULL, NULL);
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < n_formats; i++) {
|
|
|
|
|
format = pa_format_info_new();
|
|
|
|
|
if (pa_tagstruct_get_format_info(t, format) < 0) {
|
|
|
|
|
protocol_error(c);
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
pa_idxset_put(formats, format, NULL);
|
|
|
|
|
}
|
2011-05-17 22:31:10 +01:00
|
|
|
|
|
|
|
|
if (pa_tagstruct_get_cvolume(t, &volume) < 0 ||
|
|
|
|
|
pa_tagstruct_get_boolean(t, &muted) < 0 ||
|
|
|
|
|
pa_tagstruct_get_boolean(t, &volume_set) < 0 ||
|
|
|
|
|
pa_tagstruct_get_boolean(t, &muted_set) < 0 ||
|
|
|
|
|
pa_tagstruct_get_boolean(t, &relative_volume) < 0 ||
|
|
|
|
|
pa_tagstruct_get_boolean(t, &passthrough) < 0) {
|
|
|
|
|
|
|
|
|
|
protocol_error(c);
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CHECK_VALIDITY_GOTO(c->pstream, pa_cvolume_valid(&volume), tag, PA_ERR_INVALID, finish);
|
2011-05-17 21:56:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (n_formats == 0) {
|
|
|
|
|
CHECK_VALIDITY_GOTO(c->pstream, pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID, finish);
|
2011-07-09 18:27:45 -07:00
|
|
|
CHECK_VALIDITY_GOTO(c->pstream, map.channels == ss.channels, tag, PA_ERR_INVALID, finish);
|
|
|
|
|
CHECK_VALIDITY_GOTO(c->pstream, c->version < 22 || (volume.channels == ss.channels), tag, PA_ERR_INVALID, finish);
|
2011-05-17 21:56:10 +01:00
|
|
|
CHECK_VALIDITY_GOTO(c->pstream, pa_channel_map_valid(&map), tag, PA_ERR_INVALID, finish);
|
|
|
|
|
} else {
|
|
|
|
|
PA_IDXSET_FOREACH(format, formats, i) {
|
|
|
|
|
CHECK_VALIDITY_GOTO(c->pstream, pa_format_info_valid(format), tag, PA_ERR_INVALID, finish);
|
2008-10-26 19:32:04 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2007-11-21 01:30:40 +00:00
|
|
|
if (!pa_tagstruct_eof(t)) {
|
|
|
|
|
protocol_error(c);
|
2011-05-17 21:56:10 +01:00
|
|
|
goto finish;
|
2007-11-21 01:30:40 +00:00
|
|
|
}
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (source_index != PA_INVALID_INDEX) {
|
|
|
|
|
|
|
|
|
|
if (!(source = pa_idxset_get_by_index(c->protocol->core->sources, source_index))) {
|
|
|
|
|
pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
|
2011-05-17 21:56:10 +01:00
|
|
|
goto finish;
|
2008-05-15 23:34:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else if (source_name) {
|
|
|
|
|
|
2009-01-15 20:07:13 +01:00
|
|
|
if (!(source = pa_namereg_get(c->protocol->core, source_name, PA_NAMEREG_SOURCE))) {
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
|
2011-05-17 21:56:10 +01:00
|
|
|
goto finish;
|
2008-05-15 23:34:41 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-13 21:56:19 +00:00
|
|
|
if (direct_on_input_idx != PA_INVALID_INDEX) {
|
|
|
|
|
|
|
|
|
|
if (!(direct_on_input = pa_idxset_get_by_index(c->protocol->core->sink_inputs, direct_on_input_idx))) {
|
|
|
|
|
pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
|
2011-05-17 21:56:10 +01:00
|
|
|
goto finish;
|
2008-06-13 21:56:19 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2007-11-21 01:30:40 +00:00
|
|
|
flags =
|
2011-03-12 19:45:02 +01:00
|
|
|
(corked ? PA_SOURCE_OUTPUT_START_CORKED : 0) |
|
|
|
|
|
(no_remap ? PA_SOURCE_OUTPUT_NO_REMAP : 0) |
|
|
|
|
|
(no_remix ? PA_SOURCE_OUTPUT_NO_REMIX : 0) |
|
|
|
|
|
(fix_format ? PA_SOURCE_OUTPUT_FIX_FORMAT : 0) |
|
|
|
|
|
(fix_rate ? PA_SOURCE_OUTPUT_FIX_RATE : 0) |
|
|
|
|
|
(fix_channels ? PA_SOURCE_OUTPUT_FIX_CHANNELS : 0) |
|
|
|
|
|
(no_move ? PA_SOURCE_OUTPUT_DONT_MOVE : 0) |
|
|
|
|
|
(variable_rate ? PA_SOURCE_OUTPUT_VARIABLE_RATE : 0) |
|
2009-02-03 02:23:46 +01:00
|
|
|
(dont_inhibit_auto_suspend ? PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND : 0) |
|
2011-05-17 21:56:10 +01:00
|
|
|
(fail_on_suspend ? PA_SOURCE_OUTPUT_NO_CREATE_ON_SUSPEND|PA_SOURCE_OUTPUT_KILL_ON_SUSPEND : 0) |
|
|
|
|
|
(passthrough ? PA_SOURCE_OUTPUT_PASSTHROUGH : 0);
|
2007-11-21 01:30:40 +00:00
|
|
|
|
2011-05-17 22:31:10 +01:00
|
|
|
s = record_stream_new(c, source, &ss, &map, formats, &attr, volume_set ? &volume : NULL, muted, muted_set, flags, p, adjust_latency, early_requests, relative_volume, peak_detect, direct_on_input, &ret);
|
2018-05-28 21:14:00 +05:30
|
|
|
/* We no longer own the formats idxset */
|
|
|
|
|
formats = NULL;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2011-05-17 21:56:10 +01:00
|
|
|
CHECK_VALIDITY_GOTO(c->pstream, s, tag, ret, finish);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-02-22 18:54:21 +00:00
|
|
|
reply = reply_new(tag);
|
2004-07-10 16:50:22 +00:00
|
|
|
pa_tagstruct_putu32(reply, s->index);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(s->source_output);
|
2004-07-10 16:50:22 +00:00
|
|
|
pa_tagstruct_putu32(reply, s->source_output->index);
|
2006-05-25 23:20:28 +00:00
|
|
|
|
|
|
|
|
if (c->version >= 9) {
|
|
|
|
|
/* Since 0.9 we support sending the buffer metrics back to the client */
|
|
|
|
|
|
2009-03-30 18:46:12 +02:00
|
|
|
pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.maxlength);
|
|
|
|
|
pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.fragsize);
|
2006-05-25 23:20:28 +00:00
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-11-21 01:30:40 +00:00
|
|
|
if (c->version >= 12) {
|
|
|
|
|
/* Since 0.9.8 we support sending the chosen sample
|
|
|
|
|
* spec/channel map/device/suspend status back to the
|
|
|
|
|
* client */
|
|
|
|
|
|
|
|
|
|
pa_tagstruct_put_sample_spec(reply, &ss);
|
|
|
|
|
pa_tagstruct_put_channel_map(reply, &map);
|
|
|
|
|
|
|
|
|
|
pa_tagstruct_putu32(reply, s->source_output->source->index);
|
|
|
|
|
pa_tagstruct_puts(reply, s->source_output->source->name);
|
|
|
|
|
|
2018-06-26 16:25:58 +03:00
|
|
|
pa_tagstruct_put_boolean(reply, s->source_output->source->state == PA_SOURCE_SUSPENDED);
|
2007-11-21 01:30:40 +00:00
|
|
|
}
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (c->version >= 13)
|
2009-04-05 02:59:02 +02:00
|
|
|
pa_tagstruct_put_usec(reply, s->configured_source_latency);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2011-05-17 21:56:10 +01:00
|
|
|
if (c->version >= 22) {
|
|
|
|
|
/* Send back the format we negotiated */
|
|
|
|
|
if (s->source_output->format)
|
|
|
|
|
pa_tagstruct_put_format_info(reply, s->source_output->format);
|
|
|
|
|
else {
|
|
|
|
|
pa_format_info *f = pa_format_info_new();
|
|
|
|
|
pa_tagstruct_put_format_info(reply, f);
|
|
|
|
|
pa_format_info_free(f);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2004-07-10 16:50:22 +00:00
|
|
|
pa_pstream_send_tagstruct(c->pstream, reply);
|
2011-05-17 21:56:10 +01:00
|
|
|
|
|
|
|
|
finish:
|
|
|
|
|
if (p)
|
|
|
|
|
pa_proplist_free(p);
|
|
|
|
|
if (formats)
|
2013-02-12 21:36:55 +02:00
|
|
|
pa_idxset_free(formats, (pa_free_cb_t) pa_format_info_free);
|
2004-07-10 16:50:22 +00:00
|
|
|
}
|
|
|
|
|
|
2008-08-09 16:20:29 +02:00
|
|
|
static void command_exit(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
2008-08-06 19:39:12 +02:00
|
|
|
int ret;
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_assert_ref(c);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(t);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-07-07 00:22:46 +00:00
|
|
|
if (!pa_tagstruct_eof(t)) {
|
|
|
|
|
protocol_error(c);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2004-06-20 01:12:13 +00:00
|
|
|
|
2006-02-22 18:54:21 +00:00
|
|
|
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
|
2013-06-27 19:28:09 +02:00
|
|
|
ret = pa_core_exit(c->protocol->core, false, 0);
|
2008-08-06 19:39:12 +02:00
|
|
|
CHECK_VALIDITY(c->pstream, ret >= 0, tag, PA_ERR_ACCESS);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2009-09-08 23:53:28 +02:00
|
|
|
pa_log_debug("Client %s asks us to terminate.", pa_strnull(pa_proplist_gets(c->client->proplist, PA_PROP_APPLICATION_PROCESS_BINARY)));
|
|
|
|
|
|
2004-07-03 23:35:12 +00:00
|
|
|
pa_pstream_send_simple_ack(c->pstream, tag); /* nonsense */
|
2004-06-20 01:12:13 +00:00
|
|
|
}
|
|
|
|
|
|
2016-04-15 23:07:36 +02:00
|
|
|
static void setup_srbchannel(pa_native_connection *c, pa_mem_type_t shm_type) {
|
2014-04-25 17:57:18 +02:00
|
|
|
pa_srbchannel_template srbt;
|
|
|
|
|
pa_srbchannel *srb;
|
|
|
|
|
pa_memchunk mc;
|
|
|
|
|
pa_tagstruct *t;
|
|
|
|
|
int fdlist[2];
|
|
|
|
|
|
2016-04-22 21:48:26 +02:00
|
|
|
#ifndef HAVE_CREDS
|
|
|
|
|
pa_log_debug("Disabling srbchannel, reason: No fd passing support");
|
|
|
|
|
return;
|
|
|
|
|
#endif
|
|
|
|
|
|
2014-04-25 17:57:18 +02:00
|
|
|
if (!c->options->srbchannel) {
|
2015-01-21 15:06:53 +01:00
|
|
|
pa_log_debug("Disabling srbchannel, reason: Must be enabled by module parameter");
|
2014-04-25 17:57:18 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (c->version < 30) {
|
|
|
|
|
pa_log_debug("Disabling srbchannel, reason: Protocol too old");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!pa_pstream_get_shm(c->pstream)) {
|
|
|
|
|
pa_log_debug("Disabling srbchannel, reason: No SHM support");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-13 00:53:34 +02:00
|
|
|
if (c->rw_mempool) {
|
|
|
|
|
pa_log_debug("Ignoring srbchannel setup, reason: received COMMAND_AUTH "
|
|
|
|
|
"more than once");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-15 23:07:36 +02:00
|
|
|
if (!(c->rw_mempool = pa_mempool_new(shm_type, c->protocol->core->shm_size, true))) {
|
2016-03-13 00:53:34 +02:00
|
|
|
pa_log_warn("Disabling srbchannel, reason: Failed to allocate shared "
|
|
|
|
|
"writable memory pool.");
|
2014-04-25 17:57:18 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2016-04-15 23:07:36 +02:00
|
|
|
|
|
|
|
|
if (shm_type == PA_MEM_TYPE_SHARED_MEMFD) {
|
|
|
|
|
const char *reason;
|
|
|
|
|
if (pa_pstream_register_memfd_mempool(c->pstream, c->rw_mempool, &reason)) {
|
|
|
|
|
pa_log_warn("Disabling srbchannel, reason: Failed to register memfd mempool: %s", reason);
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-03-13 00:53:34 +02:00
|
|
|
pa_mempool_set_is_remote_writable(c->rw_mempool, true);
|
2014-04-25 17:57:18 +02:00
|
|
|
|
2016-03-13 00:53:34 +02:00
|
|
|
srb = pa_srbchannel_new(c->protocol->core->mainloop, c->rw_mempool);
|
2014-08-12 23:33:12 +02:00
|
|
|
if (!srb) {
|
|
|
|
|
pa_log_debug("Failed to create srbchannel");
|
2016-04-15 23:07:36 +02:00
|
|
|
goto fail;
|
2014-08-12 23:33:12 +02:00
|
|
|
}
|
|
|
|
|
pa_log_debug("Enabling srbchannel...");
|
2014-04-25 17:57:18 +02:00
|
|
|
pa_srbchannel_export(srb, &srbt);
|
|
|
|
|
|
|
|
|
|
/* Send enable command to client */
|
2014-10-23 16:09:45 +02:00
|
|
|
t = pa_tagstruct_new();
|
2014-04-25 17:57:18 +02:00
|
|
|
pa_tagstruct_putu32(t, PA_COMMAND_ENABLE_SRBCHANNEL);
|
|
|
|
|
pa_tagstruct_putu32(t, (size_t) srb); /* tag */
|
|
|
|
|
fdlist[0] = srbt.readfd;
|
|
|
|
|
fdlist[1] = srbt.writefd;
|
2016-03-13 01:12:18 +02:00
|
|
|
pa_pstream_send_tagstruct_with_fds(c->pstream, t, 2, fdlist, false);
|
2014-04-25 17:57:18 +02:00
|
|
|
|
|
|
|
|
/* Send ringbuffer memblock to client */
|
|
|
|
|
mc.memblock = srbt.memblock;
|
|
|
|
|
mc.index = 0;
|
|
|
|
|
mc.length = pa_memblock_get_length(srbt.memblock);
|
|
|
|
|
pa_pstream_send_memblock(c->pstream, 0, 0, 0, &mc);
|
|
|
|
|
|
|
|
|
|
c->srbpending = srb;
|
2016-04-15 23:07:36 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
fail:
|
|
|
|
|
if (c->rw_mempool) {
|
|
|
|
|
pa_mempool_unref(c->rw_mempool);
|
|
|
|
|
c->rw_mempool = NULL;
|
|
|
|
|
}
|
2014-04-25 17:57:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void command_enable_srbchannel(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
|
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
|
|
|
|
|
2015-09-14 17:50:36 +02:00
|
|
|
if (tag != (uint32_t) (size_t) c->srbpending) {
|
2014-04-25 17:57:18 +02:00
|
|
|
protocol_error(c);
|
2015-09-14 17:50:36 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2014-04-25 17:57:18 +02:00
|
|
|
|
|
|
|
|
pa_log_debug("Client enabled srbchannel.");
|
|
|
|
|
pa_pstream_set_srbchannel(c->pstream, c->srbpending);
|
|
|
|
|
c->srbpending = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-09 16:20:29 +02:00
|
|
|
static void command_auth(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
2004-07-06 00:08:44 +00:00
|
|
|
const void*cookie;
|
2016-04-15 23:07:36 +02:00
|
|
|
bool memfd_on_remote = false, do_memfd = false;
|
2006-03-02 21:56:15 +00:00
|
|
|
pa_tagstruct *reply;
|
2016-04-15 23:06:02 +02:00
|
|
|
pa_mem_type_t shm_type;
|
2013-06-27 19:28:09 +02:00
|
|
|
bool shm_on_remote = false, do_shm;
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_assert_ref(c);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(t);
|
2004-07-06 00:08:44 +00:00
|
|
|
|
2006-03-02 21:56:15 +00:00
|
|
|
if (pa_tagstruct_getu32(t, &c->version) < 0 ||
|
|
|
|
|
pa_tagstruct_get_arbitrary(t, &cookie, PA_NATIVE_COOKIE_LENGTH) < 0 ||
|
2004-07-07 00:22:46 +00:00
|
|
|
!pa_tagstruct_eof(t)) {
|
|
|
|
|
protocol_error(c);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2004-09-12 23:40:53 +00:00
|
|
|
|
2006-03-02 21:56:15 +00:00
|
|
|
/* Minimum supported version */
|
|
|
|
|
if (c->version < 8) {
|
|
|
|
|
pa_pstream_send_error(c->pstream, tag, PA_ERR_VERSION);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-17 18:29:00 +00:00
|
|
|
/* Starting with protocol version 13 the MSB of the version tag
|
2008-08-04 18:58:29 +02:00
|
|
|
reflects if shm is available for this pa_native_connection or
|
2008-06-17 18:29:00 +00:00
|
|
|
not. */
|
2016-04-25 18:12:39 +05:30
|
|
|
if ((c->version & PA_PROTOCOL_VERSION_MASK) >= 13) {
|
|
|
|
|
shm_on_remote = !!(c->version & PA_PROTOCOL_FLAG_SHM);
|
2016-04-15 23:06:02 +02:00
|
|
|
|
|
|
|
|
/* Starting with protocol version 31, the second MSB of the version
|
|
|
|
|
* tag reflects whether memfd is supported on the other PA end. */
|
2016-04-25 18:12:39 +05:30
|
|
|
if ((c->version & PA_PROTOCOL_VERSION_MASK) >= 31)
|
|
|
|
|
memfd_on_remote = !!(c->version & PA_PROTOCOL_FLAG_MEMFD);
|
2016-04-15 23:06:02 +02:00
|
|
|
|
|
|
|
|
/* Reserve the two most-significant _bytes_ of the version tag
|
|
|
|
|
* for flags. */
|
2016-04-25 18:12:39 +05:30
|
|
|
c->version &= PA_PROTOCOL_VERSION_MASK;
|
2008-06-17 18:29:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pa_log_debug("Protocol version: remote %u, local %u", c->version, PA_PROTOCOL_VERSION);
|
|
|
|
|
|
|
|
|
|
pa_proplist_setf(c->client->proplist, "native-protocol.version", "%u", c->version);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2004-09-12 23:40:53 +00:00
|
|
|
if (!c->authorized) {
|
2013-06-27 19:28:09 +02:00
|
|
|
bool success = false;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-07-19 21:48:35 +00:00
|
|
|
#ifdef HAVE_CREDS
|
|
|
|
|
const pa_creds *creds;
|
2006-02-24 15:12:42 +00:00
|
|
|
|
2006-07-19 21:48:35 +00:00
|
|
|
if ((creds = pa_pdispatch_creds(pd))) {
|
|
|
|
|
if (creds->uid == getuid())
|
2013-06-27 19:28:09 +02:00
|
|
|
success = true;
|
2008-08-03 16:44:38 +02:00
|
|
|
else if (c->options->auth_group) {
|
2006-02-24 17:14:23 +00:00
|
|
|
int r;
|
2006-07-19 21:48:35 +00:00
|
|
|
gid_t gid;
|
|
|
|
|
|
2008-08-03 16:44:38 +02:00
|
|
|
if ((gid = pa_get_gid_of_group(c->options->auth_group)) == (gid_t) -1)
|
|
|
|
|
pa_log_warn("Failed to get GID of group '%s'", c->options->auth_group);
|
2006-07-19 21:48:35 +00:00
|
|
|
else if (gid == creds->gid)
|
2013-06-27 19:28:09 +02:00
|
|
|
success = true;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-07-19 21:48:35 +00:00
|
|
|
if (!success) {
|
2008-08-03 16:44:38 +02:00
|
|
|
if ((r = pa_uid_in_group(creds->uid, c->options->auth_group)) < 0)
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_log_warn("Failed to check group membership.");
|
2006-07-19 21:48:35 +00:00
|
|
|
else if (r > 0)
|
2013-06-27 19:28:09 +02:00
|
|
|
success = true;
|
2006-07-19 21:48:35 +00:00
|
|
|
}
|
2006-02-24 17:14:23 +00:00
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-08-18 21:38:40 +00:00
|
|
|
pa_log_info("Got credentials: uid=%lu gid=%lu success=%i",
|
2006-07-19 21:48:35 +00:00
|
|
|
(unsigned long) creds->uid,
|
|
|
|
|
(unsigned long) creds->gid,
|
2008-06-17 18:29:00 +00:00
|
|
|
(int) success);
|
2006-02-24 15:12:42 +00:00
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2008-08-03 16:44:38 +02:00
|
|
|
if (!success && c->options->auth_cookie) {
|
|
|
|
|
const uint8_t *ac;
|
|
|
|
|
|
|
|
|
|
if ((ac = pa_auth_cookie_read(c->options->auth_cookie, PA_NATIVE_COOKIE_LENGTH)))
|
|
|
|
|
if (memcmp(ac, cookie, PA_NATIVE_COOKIE_LENGTH) == 0)
|
2013-06-27 19:28:09 +02:00
|
|
|
success = true;
|
2008-08-03 16:44:38 +02:00
|
|
|
}
|
2006-02-24 15:12:42 +00:00
|
|
|
|
|
|
|
|
if (!success) {
|
2014-09-02 11:50:39 +02:00
|
|
|
pa_log_warn("Denied access to client with invalid authentication data.");
|
2006-02-20 04:05:16 +00:00
|
|
|
pa_pstream_send_error(c->pstream, tag, PA_ERR_ACCESS);
|
2004-09-12 23:40:53 +00:00
|
|
|
return;
|
|
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2013-06-27 19:28:09 +02:00
|
|
|
c->authorized = true;
|
2004-11-18 00:28:26 +00:00
|
|
|
if (c->auth_timeout_event) {
|
|
|
|
|
c->protocol->core->mainloop->time_free(c->auth_timeout_event);
|
|
|
|
|
c->auth_timeout_event = NULL;
|
|
|
|
|
}
|
2004-07-06 00:08:44 +00:00
|
|
|
}
|
2006-03-02 21:56:15 +00:00
|
|
|
|
2016-04-15 23:07:36 +02:00
|
|
|
/* Enable shared memory and memfd support if possible */
|
2008-06-17 18:29:00 +00:00
|
|
|
do_shm =
|
|
|
|
|
pa_mempool_is_shared(c->protocol->core->mempool) &&
|
|
|
|
|
c->is_local;
|
|
|
|
|
|
|
|
|
|
pa_log_debug("SHM possible: %s", pa_yes_no(do_shm));
|
|
|
|
|
|
|
|
|
|
if (do_shm)
|
|
|
|
|
if (c->version < 10 || (c->version >= 13 && !shm_on_remote))
|
2013-06-27 19:28:09 +02:00
|
|
|
do_shm = false;
|
2008-06-17 18:29:00 +00:00
|
|
|
|
2008-10-01 01:31:56 +02:00
|
|
|
#ifdef HAVE_CREDS
|
2008-06-17 18:29:00 +00:00
|
|
|
if (do_shm) {
|
|
|
|
|
/* Only enable SHM if both sides are owned by the same
|
|
|
|
|
* user. This is a security measure because otherwise data
|
|
|
|
|
* private to the user might leak. */
|
|
|
|
|
|
|
|
|
|
const pa_creds *creds;
|
|
|
|
|
if (!(creds = pa_pdispatch_creds(pd)) || getuid() != creds->uid)
|
2013-06-27 19:28:09 +02:00
|
|
|
do_shm = false;
|
2008-06-17 18:29:00 +00:00
|
|
|
}
|
2008-10-01 01:31:56 +02:00
|
|
|
#endif
|
2008-06-17 18:29:00 +00:00
|
|
|
|
|
|
|
|
pa_log_debug("Negotiated SHM: %s", pa_yes_no(do_shm));
|
|
|
|
|
pa_pstream_enable_shm(c->pstream, do_shm);
|
|
|
|
|
|
2016-11-19 01:54:10 +02:00
|
|
|
/* Do not declare memfd support for 9.0 client libraries (protocol v31).
|
|
|
|
|
*
|
|
|
|
|
* Although they support memfd transport, such 9.0 clients has an iochannel
|
|
|
|
|
* bug that would break memfd audio if they're run in 32-bit mode over a
|
|
|
|
|
* 64-bit kernel. Thus influence them to use the POSIX shared memory model
|
|
|
|
|
* instead. Check commit 451d1d676237c81 for further details. */
|
2016-04-15 23:07:36 +02:00
|
|
|
do_memfd =
|
2016-11-19 01:54:10 +02:00
|
|
|
c->version >= 32 && do_shm && pa_mempool_is_memfd_backed(c->protocol->core->mempool);
|
2016-04-15 23:07:36 +02:00
|
|
|
|
2016-04-15 23:06:02 +02:00
|
|
|
shm_type = PA_MEM_TYPE_PRIVATE;
|
|
|
|
|
if (do_shm) {
|
2016-11-19 01:54:10 +02:00
|
|
|
if (do_memfd && memfd_on_remote) {
|
2016-04-15 23:06:02 +02:00
|
|
|
pa_pstream_enable_memfd(c->pstream);
|
|
|
|
|
shm_type = PA_MEM_TYPE_SHARED_MEMFD;
|
|
|
|
|
} else
|
|
|
|
|
shm_type = PA_MEM_TYPE_SHARED_POSIX;
|
|
|
|
|
|
|
|
|
|
pa_log_debug("Memfd possible: %s", pa_yes_no(pa_memfd_is_locally_supported()));
|
|
|
|
|
pa_log_debug("Negotiated SHM type: %s", pa_mem_type_to_string(shm_type));
|
|
|
|
|
}
|
|
|
|
|
|
2006-03-02 21:56:15 +00:00
|
|
|
reply = reply_new(tag);
|
2016-04-15 23:06:02 +02:00
|
|
|
pa_tagstruct_putu32(reply, PA_PROTOCOL_VERSION | (do_shm ? 0x80000000 : 0) |
|
2016-04-15 23:07:36 +02:00
|
|
|
(do_memfd ? 0x40000000 : 0));
|
2006-08-18 23:44:35 +00:00
|
|
|
|
|
|
|
|
#ifdef HAVE_CREDS
|
|
|
|
|
{
|
|
|
|
|
/* SHM support is only enabled after both sides made sure they are the same user. */
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-08-18 23:44:35 +00:00
|
|
|
pa_creds ucred;
|
|
|
|
|
|
|
|
|
|
ucred.uid = getuid();
|
|
|
|
|
ucred.gid = getgid();
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-08-18 23:44:35 +00:00
|
|
|
pa_pstream_send_tagstruct_with_creds(c->pstream, reply, &ucred);
|
|
|
|
|
}
|
|
|
|
|
#else
|
2006-03-02 21:56:15 +00:00
|
|
|
pa_pstream_send_tagstruct(c->pstream, reply);
|
2006-08-18 23:44:35 +00:00
|
|
|
#endif
|
2014-04-25 17:57:18 +02:00
|
|
|
|
2016-04-15 23:07:36 +02:00
|
|
|
/* The client enables memfd transport on its pstream only after
|
|
|
|
|
* inspecting our version flags to see if we support memfds too.
|
|
|
|
|
*
|
|
|
|
|
* Thus register any pools after sending the server's version
|
|
|
|
|
* flags and _never_ before it. */
|
|
|
|
|
if (shm_type == PA_MEM_TYPE_SHARED_MEMFD) {
|
|
|
|
|
const char *reason;
|
|
|
|
|
|
|
|
|
|
if (pa_pstream_register_memfd_mempool(c->pstream, c->protocol->core->mempool, &reason))
|
|
|
|
|
pa_log("Failed to register memfd mempool. Reason: %s", reason);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setup_srbchannel(c, shm_type);
|
2004-07-06 00:08:44 +00:00
|
|
|
}
|
|
|
|
|
|
2016-03-13 01:12:18 +02:00
|
|
|
static void command_register_memfd_shmid(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
|
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
|
|
|
|
|
|
|
|
|
pa_native_connection_assert_ref(c);
|
|
|
|
|
pa_assert(t);
|
|
|
|
|
|
|
|
|
|
if (pa_common_command_register_memfd_shmid(c->pstream, pd, c->version, command, t))
|
|
|
|
|
protocol_error(c);
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-09 16:20:29 +02:00
|
|
|
static void command_set_client_name(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
2008-05-15 23:34:41 +00:00
|
|
|
const char *name = NULL;
|
|
|
|
|
pa_proplist *p;
|
|
|
|
|
pa_tagstruct *reply;
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_assert_ref(c);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(t);
|
2004-07-06 00:08:44 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
p = pa_proplist_new();
|
|
|
|
|
|
|
|
|
|
if ((c->version < 13 && pa_tagstruct_gets(t, &name) < 0) ||
|
|
|
|
|
(c->version >= 13 && pa_tagstruct_get_proplist(t, p) < 0) ||
|
2004-07-07 00:22:46 +00:00
|
|
|
!pa_tagstruct_eof(t)) {
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2004-07-07 00:22:46 +00:00
|
|
|
protocol_error(c);
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_proplist_free(p);
|
2004-07-07 00:22:46 +00:00
|
|
|
return;
|
|
|
|
|
}
|
2004-07-06 00:08:44 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (name)
|
|
|
|
|
if (pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, name) < 0) {
|
|
|
|
|
pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
|
|
|
|
|
pa_proplist_free(p);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2009-02-05 04:10:08 +01:00
|
|
|
pa_client_update_proplist(c->client, PA_UPDATE_REPLACE, p);
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_proplist_free(p);
|
|
|
|
|
|
|
|
|
|
reply = reply_new(tag);
|
|
|
|
|
|
|
|
|
|
if (c->version >= 13)
|
|
|
|
|
pa_tagstruct_putu32(reply, c->client->index);
|
|
|
|
|
|
|
|
|
|
pa_pstream_send_tagstruct(c->pstream, reply);
|
2004-07-07 00:22:46 +00:00
|
|
|
}
|
|
|
|
|
|
2008-08-09 16:20:29 +02:00
|
|
|
static void command_lookup(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
2004-07-07 00:22:46 +00:00
|
|
|
const char *name;
|
2006-01-11 01:17:39 +00:00
|
|
|
uint32_t idx = PA_IDXSET_INVALID;
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_assert_ref(c);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(t);
|
2004-07-07 00:22:46 +00:00
|
|
|
|
2006-02-22 18:54:21 +00:00
|
|
|
if (pa_tagstruct_gets(t, &name) < 0 ||
|
2004-07-07 00:22:46 +00:00
|
|
|
!pa_tagstruct_eof(t)) {
|
|
|
|
|
protocol_error(c);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-22 18:54:21 +00:00
|
|
|
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
|
2009-08-29 03:57:53 +02:00
|
|
|
CHECK_VALIDITY(c->pstream, name && pa_namereg_is_valid_name_or_wildcard(name, command == PA_COMMAND_LOOKUP_SINK ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE), tag, PA_ERR_INVALID);
|
2004-07-07 22:02:15 +00:00
|
|
|
|
2004-07-07 00:22:46 +00:00
|
|
|
if (command == PA_COMMAND_LOOKUP_SINK) {
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_sink *sink;
|
2009-01-15 20:07:13 +01:00
|
|
|
if ((sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK)))
|
2006-01-11 01:17:39 +00:00
|
|
|
idx = sink->index;
|
2004-07-07 00:22:46 +00:00
|
|
|
} else {
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_source *source;
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(command == PA_COMMAND_LOOKUP_SOURCE);
|
2009-01-15 20:07:13 +01:00
|
|
|
if ((source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE)))
|
2006-01-11 01:17:39 +00:00
|
|
|
idx = source->index;
|
2004-07-07 00:22:46 +00:00
|
|
|
}
|
|
|
|
|
|
2006-01-11 01:17:39 +00:00
|
|
|
if (idx == PA_IDXSET_INVALID)
|
2006-02-20 04:05:16 +00:00
|
|
|
pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
|
2004-07-07 00:22:46 +00:00
|
|
|
else {
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_tagstruct *reply;
|
2006-02-22 18:54:21 +00:00
|
|
|
reply = reply_new(tag);
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_tagstruct_putu32(reply, idx);
|
2004-07-07 00:22:46 +00:00
|
|
|
pa_pstream_send_tagstruct(c->pstream, reply);
|
|
|
|
|
}
|
2004-07-06 00:08:44 +00:00
|
|
|
}
|
|
|
|
|
|
2008-08-09 16:20:29 +02:00
|
|
|
static void command_drain_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
2006-01-11 01:17:39 +00:00
|
|
|
uint32_t idx;
|
2007-10-28 19:13:50 +00:00
|
|
|
playback_stream *s;
|
|
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_assert_ref(c);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(t);
|
2004-07-07 22:02:15 +00:00
|
|
|
|
2006-01-11 01:17:39 +00:00
|
|
|
if (pa_tagstruct_getu32(t, &idx) < 0 ||
|
2004-07-07 22:02:15 +00:00
|
|
|
!pa_tagstruct_eof(t)) {
|
|
|
|
|
protocol_error(c);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-22 18:54:21 +00:00
|
|
|
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
|
|
|
|
|
s = pa_idxset_get_by_index(c->output_streams, idx);
|
|
|
|
|
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
|
2007-10-28 19:13:50 +00:00
|
|
|
CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_asyncmsgq_post(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_DRAIN, PA_UINT_TO_PTR(tag), 0, NULL, NULL);
|
2007-01-04 13:43:45 +00:00
|
|
|
}
|
2004-07-07 22:02:15 +00:00
|
|
|
|
2008-08-09 16:20:29 +02:00
|
|
|
static void command_stat(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_tagstruct *reply;
|
2006-08-18 19:55:18 +00:00
|
|
|
const pa_mempool_stat *stat;
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_assert_ref(c);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(t);
|
2004-07-15 20:51:55 +00:00
|
|
|
|
|
|
|
|
if (!pa_tagstruct_eof(t)) {
|
|
|
|
|
protocol_error(c);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-22 18:54:21 +00:00
|
|
|
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
|
2004-07-15 20:51:55 +00:00
|
|
|
|
2006-08-18 19:55:18 +00:00
|
|
|
stat = pa_mempool_get_stat(c->protocol->core->mempool);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-02-22 18:54:21 +00:00
|
|
|
reply = reply_new(tag);
|
2007-05-27 20:38:14 +00:00
|
|
|
pa_tagstruct_putu32(reply, (uint32_t) pa_atomic_load(&stat->n_allocated));
|
|
|
|
|
pa_tagstruct_putu32(reply, (uint32_t) pa_atomic_load(&stat->allocated_size));
|
|
|
|
|
pa_tagstruct_putu32(reply, (uint32_t) pa_atomic_load(&stat->n_accumulated));
|
|
|
|
|
pa_tagstruct_putu32(reply, (uint32_t) pa_atomic_load(&stat->accumulated_size));
|
2008-08-19 22:39:54 +02:00
|
|
|
pa_tagstruct_putu32(reply, (uint32_t) pa_scache_total_size(c->protocol->core));
|
2004-07-15 20:51:55 +00:00
|
|
|
pa_pstream_send_tagstruct(c->pstream, reply);
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-09 16:20:29 +02:00
|
|
|
static void command_get_playback_latency(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_tagstruct *reply;
|
2007-10-28 19:13:50 +00:00
|
|
|
playback_stream *s;
|
2004-09-10 22:35:12 +00:00
|
|
|
struct timeval tv, now;
|
2006-01-11 01:17:39 +00:00
|
|
|
uint32_t idx;
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_assert_ref(c);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(t);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-01-11 01:17:39 +00:00
|
|
|
if (pa_tagstruct_getu32(t, &idx) < 0 ||
|
2004-09-10 22:35:12 +00:00
|
|
|
pa_tagstruct_get_timeval(t, &tv) < 0 ||
|
2004-07-15 21:18:18 +00:00
|
|
|
!pa_tagstruct_eof(t)) {
|
|
|
|
|
protocol_error(c);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-22 18:54:21 +00:00
|
|
|
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
|
|
|
|
|
s = pa_idxset_get_by_index(c->output_streams, idx);
|
|
|
|
|
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
|
2007-10-28 19:13:50 +00:00
|
|
|
CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
|
2006-04-13 18:27:35 +00:00
|
|
|
|
2009-04-05 02:59:02 +02:00
|
|
|
/* Get an atomic snapshot of all timing parameters */
|
|
|
|
|
pa_assert_se(pa_asyncmsgq_send(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_UPDATE_LATENCY, s, 0, NULL) == 0);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2009-04-05 02:59:02 +02:00
|
|
|
reply = reply_new(tag);
|
|
|
|
|
pa_tagstruct_put_usec(reply,
|
|
|
|
|
s->current_sink_latency +
|
2009-07-23 13:40:23 +02:00
|
|
|
pa_bytes_to_usec(s->render_memblockq_length, &s->sink_input->sink->sample_spec));
|
2004-09-16 00:05:56 +00:00
|
|
|
pa_tagstruct_put_usec(reply, 0);
|
2009-04-05 02:59:02 +02:00
|
|
|
pa_tagstruct_put_boolean(reply,
|
|
|
|
|
s->playing_for > 0 &&
|
2018-06-26 16:25:58 +03:00
|
|
|
s->sink_input->sink->state == PA_SINK_RUNNING &&
|
2018-06-26 16:25:57 +03:00
|
|
|
s->sink_input->state == PA_SINK_INPUT_RUNNING);
|
2004-09-10 22:35:12 +00:00
|
|
|
pa_tagstruct_put_timeval(reply, &tv);
|
2006-04-07 00:23:38 +00:00
|
|
|
pa_tagstruct_put_timeval(reply, pa_gettimeofday(&now));
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_tagstruct_puts64(reply, s->write_index);
|
|
|
|
|
pa_tagstruct_puts64(reply, s->read_index);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
|
|
|
|
if (c->version >= 13) {
|
2009-04-05 02:59:02 +02:00
|
|
|
pa_tagstruct_putu64(reply, s->underrun_for);
|
|
|
|
|
pa_tagstruct_putu64(reply, s->playing_for);
|
2008-05-15 23:34:41 +00:00
|
|
|
}
|
|
|
|
|
|
2004-07-15 21:18:18 +00:00
|
|
|
pa_pstream_send_tagstruct(c->pstream, reply);
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-09 16:20:29 +02:00
|
|
|
static void command_get_record_latency(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_tagstruct *reply;
|
2007-10-28 19:13:50 +00:00
|
|
|
record_stream *s;
|
2004-09-16 00:05:56 +00:00
|
|
|
struct timeval tv, now;
|
2006-01-11 01:17:39 +00:00
|
|
|
uint32_t idx;
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_assert_ref(c);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(t);
|
2004-09-16 00:05:56 +00:00
|
|
|
|
2006-01-11 01:17:39 +00:00
|
|
|
if (pa_tagstruct_getu32(t, &idx) < 0 ||
|
2004-09-16 00:05:56 +00:00
|
|
|
pa_tagstruct_get_timeval(t, &tv) < 0 ||
|
|
|
|
|
!pa_tagstruct_eof(t)) {
|
|
|
|
|
protocol_error(c);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-22 18:54:21 +00:00
|
|
|
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
|
|
|
|
|
s = pa_idxset_get_by_index(c->record_streams, idx);
|
|
|
|
|
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
|
2004-09-16 00:05:56 +00:00
|
|
|
|
2009-04-05 02:59:02 +02:00
|
|
|
/* Get an atomic snapshot of all timing parameters */
|
|
|
|
|
pa_assert_se(pa_asyncmsgq_send(s->source_output->source->asyncmsgq, PA_MSGOBJECT(s->source_output), SOURCE_OUTPUT_MESSAGE_UPDATE_LATENCY, s, 0, NULL) == 0);
|
|
|
|
|
|
2006-02-22 18:54:21 +00:00
|
|
|
reply = reply_new(tag);
|
2009-04-05 02:59:02 +02:00
|
|
|
pa_tagstruct_put_usec(reply, s->current_monitor_latency);
|
|
|
|
|
pa_tagstruct_put_usec(reply,
|
|
|
|
|
s->current_source_latency +
|
2017-07-11 21:16:17 +03:00
|
|
|
pa_bytes_to_usec(s->on_the_fly_snapshot, &s->source_output->sample_spec));
|
2009-04-05 02:59:02 +02:00
|
|
|
pa_tagstruct_put_boolean(reply,
|
2018-06-26 16:25:58 +03:00
|
|
|
s->source_output->source->state == PA_SOURCE_RUNNING &&
|
2018-06-26 16:25:57 +03:00
|
|
|
s->source_output->state == PA_SOURCE_OUTPUT_RUNNING);
|
2004-09-16 00:05:56 +00:00
|
|
|
pa_tagstruct_put_timeval(reply, &tv);
|
2006-04-07 00:23:38 +00:00
|
|
|
pa_tagstruct_put_timeval(reply, pa_gettimeofday(&now));
|
2006-03-02 14:22:25 +00:00
|
|
|
pa_tagstruct_puts64(reply, pa_memblockq_get_write_index(s->memblockq));
|
|
|
|
|
pa_tagstruct_puts64(reply, pa_memblockq_get_read_index(s->memblockq));
|
2004-09-16 00:05:56 +00:00
|
|
|
pa_pstream_send_tagstruct(c->pstream, reply);
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-09 16:20:29 +02:00
|
|
|
static void command_create_upload_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
2007-10-28 19:13:50 +00:00
|
|
|
upload_stream *s;
|
2006-02-27 09:09:15 +00:00
|
|
|
uint32_t length;
|
2008-05-15 23:34:41 +00:00
|
|
|
const char *name = NULL;
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_sample_spec ss;
|
2006-01-27 16:25:31 +00:00
|
|
|
pa_channel_map map;
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_tagstruct *reply;
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_proplist *p;
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_assert_ref(c);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(t);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-05-27 22:07:27 +00:00
|
|
|
if (pa_tagstruct_gets(t, &name) < 0 ||
|
2004-08-03 19:26:56 +00:00
|
|
|
pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
|
2006-01-27 16:25:31 +00:00
|
|
|
pa_tagstruct_get_channel_map(t, &map) < 0 ||
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_tagstruct_getu32(t, &length) < 0) {
|
2004-08-03 19:26:56 +00:00
|
|
|
protocol_error(c);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-22 18:54:21 +00:00
|
|
|
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
|
2006-04-23 21:01:44 +00:00
|
|
|
CHECK_VALIDITY(c->pstream, pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID);
|
|
|
|
|
CHECK_VALIDITY(c->pstream, pa_channel_map_valid(&map), tag, PA_ERR_INVALID);
|
|
|
|
|
CHECK_VALIDITY(c->pstream, map.channels == ss.channels, tag, PA_ERR_INVALID);
|
2006-02-22 18:54:21 +00:00
|
|
|
CHECK_VALIDITY(c->pstream, (length % pa_frame_size(&ss)) == 0 && length > 0, tag, PA_ERR_INVALID);
|
2006-04-23 21:01:44 +00:00
|
|
|
CHECK_VALIDITY(c->pstream, length <= PA_SCACHE_ENTRY_SIZE_MAX, tag, PA_ERR_TOOLARGE);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
p = pa_proplist_new();
|
|
|
|
|
|
2009-01-20 02:15:43 +01:00
|
|
|
if ((c->version >= 13 && pa_tagstruct_get_proplist(t, p) < 0) ||
|
|
|
|
|
!pa_tagstruct_eof(t)) {
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
protocol_error(c);
|
|
|
|
|
pa_proplist_free(p);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (c->version < 13)
|
|
|
|
|
pa_proplist_sets(p, PA_PROP_MEDIA_NAME, name);
|
2008-05-27 22:07:27 +00:00
|
|
|
else if (!name)
|
|
|
|
|
if (!(name = pa_proplist_gets(p, PA_PROP_EVENT_ID)))
|
|
|
|
|
name = pa_proplist_gets(p, PA_PROP_MEDIA_NAME);
|
|
|
|
|
|
2009-02-18 20:00:57 +02:00
|
|
|
if (!name || !pa_namereg_is_valid_name(name)) {
|
|
|
|
|
pa_proplist_free(p);
|
2013-06-27 19:28:09 +02:00
|
|
|
CHECK_VALIDITY(c->pstream, false, tag, PA_ERR_INVALID);
|
2009-02-18 20:00:57 +02:00
|
|
|
}
|
2008-05-15 23:34:41 +00:00
|
|
|
|
|
|
|
|
s = upload_stream_new(c, &ss, &map, name, length, p);
|
|
|
|
|
pa_proplist_free(p);
|
|
|
|
|
|
2006-02-22 18:54:21 +00:00
|
|
|
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-02-22 18:54:21 +00:00
|
|
|
reply = reply_new(tag);
|
2004-08-03 19:26:56 +00:00
|
|
|
pa_tagstruct_putu32(reply, s->index);
|
|
|
|
|
pa_tagstruct_putu32(reply, length);
|
|
|
|
|
pa_pstream_send_tagstruct(c->pstream, reply);
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-09 16:20:29 +02:00
|
|
|
static void command_finish_upload_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
2004-08-03 19:26:56 +00:00
|
|
|
uint32_t channel;
|
2007-10-28 19:13:50 +00:00
|
|
|
upload_stream *s;
|
2006-01-11 01:17:39 +00:00
|
|
|
uint32_t idx;
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_assert_ref(c);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(t);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-08-03 19:26:56 +00:00
|
|
|
if (pa_tagstruct_getu32(t, &channel) < 0 ||
|
|
|
|
|
!pa_tagstruct_eof(t)) {
|
|
|
|
|
protocol_error(c);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-22 18:54:21 +00:00
|
|
|
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
|
2004-08-03 19:26:56 +00:00
|
|
|
|
2006-02-22 18:54:21 +00:00
|
|
|
s = pa_idxset_get_by_index(c->output_streams, channel);
|
|
|
|
|
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
|
2007-10-28 19:13:50 +00:00
|
|
|
CHECK_VALIDITY(c->pstream, upload_stream_isinstance(s), tag, PA_ERR_NOENTITY);
|
2004-08-03 19:26:56 +00:00
|
|
|
|
2009-07-01 14:25:13 +02:00
|
|
|
if (!s->memchunk.memblock)
|
|
|
|
|
pa_pstream_send_error(c->pstream, tag, PA_ERR_TOOLARGE);
|
|
|
|
|
else if (pa_scache_add_item(c->protocol->core, s->name, &s->sample_spec, &s->channel_map, &s->memchunk, s->proplist, &idx) < 0)
|
2006-04-23 21:01:44 +00:00
|
|
|
pa_pstream_send_error(c->pstream, tag, PA_ERR_INTERNAL);
|
|
|
|
|
else
|
|
|
|
|
pa_pstream_send_simple_ack(c->pstream, tag);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
upload_stream_unlink(s);
|
2004-08-03 19:26:56 +00:00
|
|
|
}
|
|
|
|
|
|
2008-08-09 16:20:29 +02:00
|
|
|
static void command_play_sample(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
2006-01-27 16:25:31 +00:00
|
|
|
uint32_t sink_index;
|
2006-04-23 19:49:01 +00:00
|
|
|
pa_volume_t volume;
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_sink *sink;
|
2004-08-03 19:26:56 +00:00
|
|
|
const char *name, *sink_name;
|
2008-05-15 23:34:41 +00:00
|
|
|
uint32_t idx;
|
|
|
|
|
pa_proplist *p;
|
|
|
|
|
pa_tagstruct *reply;
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_assert_ref(c);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(t);
|
2004-08-03 19:26:56 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
|
|
|
|
|
|
2004-08-03 19:26:56 +00:00
|
|
|
if (pa_tagstruct_getu32(t, &sink_index) < 0 ||
|
|
|
|
|
pa_tagstruct_gets(t, &sink_name) < 0 ||
|
2006-04-23 19:49:01 +00:00
|
|
|
pa_tagstruct_getu32(t, &volume) < 0 ||
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_tagstruct_gets(t, &name) < 0) {
|
2004-08-03 19:26:56 +00:00
|
|
|
protocol_error(c);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2009-08-29 03:57:53 +02:00
|
|
|
CHECK_VALIDITY(c->pstream, !sink_name || pa_namereg_is_valid_name_or_wildcard(sink_name, PA_NAMEREG_SINK), tag, PA_ERR_INVALID);
|
2008-08-18 17:48:04 +02:00
|
|
|
CHECK_VALIDITY(c->pstream, sink_index == PA_INVALID_INDEX || !sink_name, tag, PA_ERR_INVALID);
|
|
|
|
|
CHECK_VALIDITY(c->pstream, !sink_name || sink_index == PA_INVALID_INDEX, tag, PA_ERR_INVALID);
|
|
|
|
|
CHECK_VALIDITY(c->pstream, name && pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID);
|
2004-08-03 19:26:56 +00:00
|
|
|
|
2006-02-22 18:54:21 +00:00
|
|
|
if (sink_index != PA_INVALID_INDEX)
|
2004-08-03 19:26:56 +00:00
|
|
|
sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index);
|
|
|
|
|
else
|
2009-01-15 20:07:13 +01:00
|
|
|
sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK);
|
2004-08-03 19:26:56 +00:00
|
|
|
|
2006-02-22 18:54:21 +00:00
|
|
|
CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
|
2004-08-03 19:26:56 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
p = pa_proplist_new();
|
|
|
|
|
|
|
|
|
|
if ((c->version >= 13 && pa_tagstruct_get_proplist(t, p) < 0) ||
|
|
|
|
|
!pa_tagstruct_eof(t)) {
|
|
|
|
|
protocol_error(c);
|
|
|
|
|
pa_proplist_free(p);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-04 23:32:52 +02:00
|
|
|
pa_proplist_update(p, PA_UPDATE_MERGE, c->client->proplist);
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (pa_scache_play_item(c->protocol->core, name, sink, volume, p, &idx) < 0) {
|
2006-02-20 04:05:16 +00:00
|
|
|
pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_proplist_free(p);
|
2004-08-03 19:26:56 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_proplist_free(p);
|
|
|
|
|
|
|
|
|
|
reply = reply_new(tag);
|
|
|
|
|
|
|
|
|
|
if (c->version >= 13)
|
|
|
|
|
pa_tagstruct_putu32(reply, idx);
|
|
|
|
|
|
|
|
|
|
pa_pstream_send_tagstruct(c->pstream, reply);
|
2004-08-03 19:26:56 +00:00
|
|
|
}
|
|
|
|
|
|
2008-08-09 16:20:29 +02:00
|
|
|
static void command_remove_sample(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
2004-08-03 19:26:56 +00:00
|
|
|
const char *name;
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_assert_ref(c);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(t);
|
2004-08-03 19:26:56 +00:00
|
|
|
|
2006-02-22 18:54:21 +00:00
|
|
|
if (pa_tagstruct_gets(t, &name) < 0 ||
|
2004-08-03 19:26:56 +00:00
|
|
|
!pa_tagstruct_eof(t)) {
|
|
|
|
|
protocol_error(c);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-22 18:54:21 +00:00
|
|
|
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
|
2008-08-18 17:48:04 +02:00
|
|
|
CHECK_VALIDITY(c->pstream, name && pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID);
|
2004-08-03 19:26:56 +00:00
|
|
|
|
|
|
|
|
if (pa_scache_remove_item(c->protocol->core, name) < 0) {
|
2006-02-20 04:05:16 +00:00
|
|
|
pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
|
2004-08-03 19:26:56 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pa_pstream_send_simple_ack(c->pstream, tag);
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
static void fixup_sample_spec(pa_native_connection *c, pa_sample_spec *fixed, const pa_sample_spec *original) {
|
2007-11-21 22:55:28 +00:00
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(fixed);
|
|
|
|
|
pa_assert(original);
|
|
|
|
|
|
|
|
|
|
*fixed = *original;
|
|
|
|
|
|
|
|
|
|
if (c->version < 12) {
|
|
|
|
|
/* Before protocol version 12 we didn't support S32 samples,
|
|
|
|
|
* so we need to lie about this to the client */
|
|
|
|
|
|
|
|
|
|
if (fixed->format == PA_SAMPLE_S32LE)
|
|
|
|
|
fixed->format = PA_SAMPLE_FLOAT32LE;
|
|
|
|
|
if (fixed->format == PA_SAMPLE_S32BE)
|
|
|
|
|
fixed->format = PA_SAMPLE_FLOAT32BE;
|
|
|
|
|
}
|
2009-01-16 03:15:39 +01:00
|
|
|
|
|
|
|
|
if (c->version < 15) {
|
2009-01-16 18:39:36 +01:00
|
|
|
if (fixed->format == PA_SAMPLE_S24LE || fixed->format == PA_SAMPLE_S24_32LE)
|
2009-01-16 03:15:39 +01:00
|
|
|
fixed->format = PA_SAMPLE_FLOAT32LE;
|
2009-01-16 18:39:36 +01:00
|
|
|
if (fixed->format == PA_SAMPLE_S24BE || fixed->format == PA_SAMPLE_S24_32BE)
|
2009-01-16 03:15:39 +01:00
|
|
|
fixed->format = PA_SAMPLE_FLOAT32BE;
|
|
|
|
|
}
|
2007-11-21 22:55:28 +00:00
|
|
|
}
|
|
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
static void sink_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sink *sink) {
|
2007-11-21 22:55:28 +00:00
|
|
|
pa_sample_spec fixed_ss;
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(t);
|
|
|
|
|
pa_sink_assert_ref(sink);
|
|
|
|
|
|
2007-11-21 22:55:28 +00:00
|
|
|
fixup_sample_spec(c, &fixed_ss, &sink->sample_spec);
|
|
|
|
|
|
2006-01-28 01:07:09 +00:00
|
|
|
pa_tagstruct_put(
|
|
|
|
|
t,
|
|
|
|
|
PA_TAG_U32, sink->index,
|
|
|
|
|
PA_TAG_STRING, sink->name,
|
2008-05-15 23:34:41 +00:00
|
|
|
PA_TAG_STRING, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)),
|
2007-11-21 22:55:28 +00:00
|
|
|
PA_TAG_SAMPLE_SPEC, &fixed_ss,
|
2006-01-28 01:07:09 +00:00
|
|
|
PA_TAG_CHANNEL_MAP, &sink->channel_map,
|
2007-10-28 19:13:50 +00:00
|
|
|
PA_TAG_U32, sink->module ? sink->module->index : PA_INVALID_INDEX,
|
2013-06-27 19:28:09 +02:00
|
|
|
PA_TAG_CVOLUME, pa_sink_get_volume(sink, false),
|
|
|
|
|
PA_TAG_BOOLEAN, pa_sink_get_mute(sink, false),
|
2006-08-12 17:06:39 +00:00
|
|
|
PA_TAG_U32, sink->monitor_source ? sink->monitor_source->index : PA_INVALID_INDEX,
|
|
|
|
|
PA_TAG_STRING, sink->monitor_source ? sink->monitor_source->name : NULL,
|
2006-01-28 01:07:09 +00:00
|
|
|
PA_TAG_USEC, pa_sink_get_latency(sink),
|
|
|
|
|
PA_TAG_STRING, sink->driver,
|
2011-09-14 07:24:19 +05:30
|
|
|
PA_TAG_U32, sink->flags & PA_SINK_CLIENT_FLAGS_MASK,
|
2006-01-28 01:07:09 +00:00
|
|
|
PA_TAG_INVALID);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
|
|
|
|
if (c->version >= 13) {
|
|
|
|
|
pa_tagstruct_put_proplist(t, sink->proplist);
|
|
|
|
|
pa_tagstruct_put_usec(t, pa_sink_get_requested_latency(sink));
|
|
|
|
|
}
|
2008-12-24 00:49:43 +01:00
|
|
|
|
2009-01-15 17:16:31 +02:00
|
|
|
if (c->version >= 15) {
|
2008-12-24 00:49:43 +01:00
|
|
|
pa_tagstruct_put_volume(t, sink->base_volume);
|
2018-06-26 16:25:58 +03:00
|
|
|
if (PA_UNLIKELY(sink->state == PA_SINK_INVALID_STATE))
|
2009-01-15 17:16:31 +02:00
|
|
|
pa_log_error("Internal sink state is invalid.");
|
2018-06-26 16:25:58 +03:00
|
|
|
pa_tagstruct_putu32(t, sink->state);
|
2009-01-27 04:39:07 +01:00
|
|
|
pa_tagstruct_putu32(t, sink->n_volume_steps);
|
2009-02-18 22:11:50 +01:00
|
|
|
pa_tagstruct_putu32(t, sink->card ? sink->card->index : PA_INVALID_INDEX);
|
2009-01-15 17:16:31 +02:00
|
|
|
}
|
2009-06-17 23:17:37 +02:00
|
|
|
|
|
|
|
|
if (c->version >= 16) {
|
2012-06-08 21:49:09 +03:00
|
|
|
void *state;
|
|
|
|
|
pa_device_port *p;
|
|
|
|
|
|
|
|
|
|
pa_tagstruct_putu32(t, pa_hashmap_size(sink->ports));
|
|
|
|
|
|
|
|
|
|
PA_HASHMAP_FOREACH(p, sink->ports, state) {
|
|
|
|
|
pa_tagstruct_puts(t, p->name);
|
|
|
|
|
pa_tagstruct_puts(t, p->description);
|
|
|
|
|
pa_tagstruct_putu32(t, p->priority);
|
2020-04-14 17:42:34 +02:00
|
|
|
if (c->version >= 24) {
|
2012-06-08 21:49:09 +03:00
|
|
|
pa_tagstruct_putu32(t, p->available);
|
2020-04-14 20:04:00 +02:00
|
|
|
if (c->version >= 34) {
|
2020-06-17 08:40:16 +03:00
|
|
|
pa_tagstruct_puts(t, p->availability_group);
|
2020-04-14 20:04:00 +02:00
|
|
|
pa_tagstruct_putu32(t, p->type);
|
|
|
|
|
}
|
2020-04-14 17:42:34 +02:00
|
|
|
}
|
2009-06-17 23:17:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pa_tagstruct_puts(t, sink->active_port ? sink->active_port->name : NULL);
|
|
|
|
|
}
|
2011-03-08 23:30:24 +05:30
|
|
|
|
|
|
|
|
if (c->version >= 21) {
|
|
|
|
|
uint32_t i;
|
|
|
|
|
pa_format_info *f;
|
|
|
|
|
pa_idxset *formats = pa_sink_get_formats(sink);
|
|
|
|
|
|
|
|
|
|
pa_tagstruct_putu8(t, (uint8_t) pa_idxset_size(formats));
|
|
|
|
|
PA_IDXSET_FOREACH(f, formats, i) {
|
|
|
|
|
pa_tagstruct_put_format_info(t, f);
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-12 21:36:55 +02:00
|
|
|
pa_idxset_free(formats, (pa_free_cb_t) pa_format_info_free);
|
2011-03-08 23:30:24 +05:30
|
|
|
}
|
2004-08-05 19:53:57 +00:00
|
|
|
}
|
|
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
static void source_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_source *source) {
|
2007-11-21 22:55:28 +00:00
|
|
|
pa_sample_spec fixed_ss;
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(t);
|
|
|
|
|
pa_source_assert_ref(source);
|
|
|
|
|
|
2007-11-21 22:55:28 +00:00
|
|
|
fixup_sample_spec(c, &fixed_ss, &source->sample_spec);
|
|
|
|
|
|
2006-02-22 14:11:23 +00:00
|
|
|
pa_tagstruct_put(
|
|
|
|
|
t,
|
|
|
|
|
PA_TAG_U32, source->index,
|
|
|
|
|
PA_TAG_STRING, source->name,
|
2008-05-15 23:34:41 +00:00
|
|
|
PA_TAG_STRING, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)),
|
2007-11-21 22:55:28 +00:00
|
|
|
PA_TAG_SAMPLE_SPEC, &fixed_ss,
|
2006-02-22 14:11:23 +00:00
|
|
|
PA_TAG_CHANNEL_MAP, &source->channel_map,
|
2007-10-28 19:13:50 +00:00
|
|
|
PA_TAG_U32, source->module ? source->module->index : PA_INVALID_INDEX,
|
2013-06-27 19:28:09 +02:00
|
|
|
PA_TAG_CVOLUME, pa_source_get_volume(source, false),
|
|
|
|
|
PA_TAG_BOOLEAN, pa_source_get_mute(source, false),
|
2006-02-22 14:11:23 +00:00
|
|
|
PA_TAG_U32, source->monitor_of ? source->monitor_of->index : PA_INVALID_INDEX,
|
|
|
|
|
PA_TAG_STRING, source->monitor_of ? source->monitor_of->name : NULL,
|
|
|
|
|
PA_TAG_USEC, pa_source_get_latency(source),
|
|
|
|
|
PA_TAG_STRING, source->driver,
|
2011-09-14 07:24:19 +05:30
|
|
|
PA_TAG_U32, source->flags & PA_SOURCE_CLIENT_FLAGS_MASK,
|
2006-02-22 14:11:23 +00:00
|
|
|
PA_TAG_INVALID);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
|
|
|
|
if (c->version >= 13) {
|
|
|
|
|
pa_tagstruct_put_proplist(t, source->proplist);
|
|
|
|
|
pa_tagstruct_put_usec(t, pa_source_get_requested_latency(source));
|
|
|
|
|
}
|
2004-08-05 19:53:57 +00:00
|
|
|
|
2009-01-15 17:16:31 +02:00
|
|
|
if (c->version >= 15) {
|
2008-12-24 00:49:43 +01:00
|
|
|
pa_tagstruct_put_volume(t, source->base_volume);
|
2018-06-26 16:25:58 +03:00
|
|
|
if (PA_UNLIKELY(source->state == PA_SOURCE_INVALID_STATE))
|
2009-01-15 17:16:31 +02:00
|
|
|
pa_log_error("Internal source state is invalid.");
|
2018-06-26 16:25:58 +03:00
|
|
|
pa_tagstruct_putu32(t, source->state);
|
2009-01-27 04:39:07 +01:00
|
|
|
pa_tagstruct_putu32(t, source->n_volume_steps);
|
2009-02-18 22:11:50 +01:00
|
|
|
pa_tagstruct_putu32(t, source->card ? source->card->index : PA_INVALID_INDEX);
|
2009-01-15 17:16:31 +02:00
|
|
|
}
|
2009-06-17 23:17:37 +02:00
|
|
|
|
|
|
|
|
if (c->version >= 16) {
|
2012-06-08 21:49:09 +03:00
|
|
|
void *state;
|
|
|
|
|
pa_device_port *p;
|
2009-06-17 23:17:37 +02:00
|
|
|
|
2012-06-08 21:49:09 +03:00
|
|
|
pa_tagstruct_putu32(t, pa_hashmap_size(source->ports));
|
2009-06-17 23:17:37 +02:00
|
|
|
|
2012-06-08 21:49:09 +03:00
|
|
|
PA_HASHMAP_FOREACH(p, source->ports, state) {
|
|
|
|
|
pa_tagstruct_puts(t, p->name);
|
|
|
|
|
pa_tagstruct_puts(t, p->description);
|
|
|
|
|
pa_tagstruct_putu32(t, p->priority);
|
2020-04-14 17:42:34 +02:00
|
|
|
if (c->version >= 24) {
|
2012-06-08 21:49:09 +03:00
|
|
|
pa_tagstruct_putu32(t, p->available);
|
2020-04-14 20:04:00 +02:00
|
|
|
if (c->version >= 34) {
|
2020-06-17 08:40:16 +03:00
|
|
|
pa_tagstruct_puts(t, p->availability_group);
|
2020-04-14 20:04:00 +02:00
|
|
|
pa_tagstruct_putu32(t, p->type);
|
|
|
|
|
}
|
2020-04-14 17:42:34 +02:00
|
|
|
}
|
2009-06-17 23:17:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pa_tagstruct_puts(t, source->active_port ? source->active_port->name : NULL);
|
|
|
|
|
}
|
2011-05-16 22:15:57 +01:00
|
|
|
|
|
|
|
|
if (c->version >= 22) {
|
|
|
|
|
uint32_t i;
|
|
|
|
|
pa_format_info *f;
|
|
|
|
|
pa_idxset *formats = pa_source_get_formats(source);
|
|
|
|
|
|
|
|
|
|
pa_tagstruct_putu8(t, (uint8_t) pa_idxset_size(formats));
|
|
|
|
|
PA_IDXSET_FOREACH(f, formats, i) {
|
|
|
|
|
pa_tagstruct_put_format_info(t, f);
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-12 21:36:55 +02:00
|
|
|
pa_idxset_free(formats, (pa_free_cb_t) pa_format_info_free);
|
2011-05-16 22:15:57 +01:00
|
|
|
}
|
2008-12-24 00:49:43 +01:00
|
|
|
}
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
static void client_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_client *client) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(t);
|
|
|
|
|
pa_assert(client);
|
|
|
|
|
|
2004-08-12 23:25:28 +00:00
|
|
|
pa_tagstruct_putu32(t, client->index);
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_tagstruct_puts(t, pa_strnull(pa_proplist_gets(client->proplist, PA_PROP_APPLICATION_NAME)));
|
|
|
|
|
pa_tagstruct_putu32(t, client->module ? client->module->index : PA_INVALID_INDEX);
|
2006-01-27 16:25:31 +00:00
|
|
|
pa_tagstruct_puts(t, client->driver);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
|
|
|
|
if (c->version >= 13)
|
|
|
|
|
pa_tagstruct_put_proplist(t, client->proplist);
|
2004-08-12 23:25:28 +00:00
|
|
|
}
|
|
|
|
|
|
2009-01-20 03:25:29 +01:00
|
|
|
static void card_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_card *card) {
|
|
|
|
|
void *state = NULL;
|
|
|
|
|
pa_card_profile *p;
|
2012-06-08 21:49:12 +03:00
|
|
|
pa_device_port *port;
|
2009-01-20 03:25:29 +01:00
|
|
|
|
|
|
|
|
pa_assert(t);
|
|
|
|
|
pa_assert(card);
|
|
|
|
|
|
|
|
|
|
pa_tagstruct_putu32(t, card->index);
|
|
|
|
|
pa_tagstruct_puts(t, card->name);
|
|
|
|
|
pa_tagstruct_putu32(t, card->module ? card->module->index : PA_INVALID_INDEX);
|
|
|
|
|
pa_tagstruct_puts(t, card->driver);
|
|
|
|
|
|
2012-06-08 21:49:10 +03:00
|
|
|
pa_tagstruct_putu32(t, pa_hashmap_size(card->profiles));
|
2009-01-20 03:25:29 +01:00
|
|
|
|
2012-06-08 21:49:10 +03:00
|
|
|
PA_HASHMAP_FOREACH(p, card->profiles, state) {
|
|
|
|
|
pa_tagstruct_puts(t, p->name);
|
|
|
|
|
pa_tagstruct_puts(t, p->description);
|
|
|
|
|
pa_tagstruct_putu32(t, p->n_sinks);
|
|
|
|
|
pa_tagstruct_putu32(t, p->n_sources);
|
|
|
|
|
pa_tagstruct_putu32(t, p->priority);
|
2013-07-15 18:21:25 +03:00
|
|
|
|
|
|
|
|
if (c->version >= 29)
|
|
|
|
|
pa_tagstruct_putu32(t, (p->available != PA_AVAILABLE_NO));
|
2009-01-20 03:25:29 +01:00
|
|
|
}
|
|
|
|
|
|
2012-06-08 21:49:10 +03:00
|
|
|
pa_tagstruct_puts(t, card->active_profile->name);
|
2009-01-20 03:25:29 +01:00
|
|
|
pa_tagstruct_put_proplist(t, card->proplist);
|
2012-02-23 07:17:06 +01:00
|
|
|
|
|
|
|
|
if (c->version < 26)
|
|
|
|
|
return;
|
|
|
|
|
|
2012-06-08 21:49:12 +03:00
|
|
|
pa_tagstruct_putu32(t, pa_hashmap_size(card->ports));
|
2012-02-23 07:17:06 +01:00
|
|
|
|
2012-06-08 21:49:12 +03:00
|
|
|
PA_HASHMAP_FOREACH(port, card->ports, state) {
|
|
|
|
|
void *state2;
|
2012-02-23 07:17:06 +01:00
|
|
|
|
2012-06-08 21:49:12 +03:00
|
|
|
pa_tagstruct_puts(t, port->name);
|
|
|
|
|
pa_tagstruct_puts(t, port->description);
|
|
|
|
|
pa_tagstruct_putu32(t, port->priority);
|
|
|
|
|
pa_tagstruct_putu32(t, port->available);
|
2012-06-29 18:04:59 +03:00
|
|
|
pa_tagstruct_putu8(t, port->direction);
|
2012-06-08 21:49:12 +03:00
|
|
|
pa_tagstruct_put_proplist(t, port->proplist);
|
2012-06-08 21:49:11 +03:00
|
|
|
|
2012-06-08 21:49:12 +03:00
|
|
|
pa_tagstruct_putu32(t, pa_hashmap_size(port->profiles));
|
2012-02-23 07:17:06 +01:00
|
|
|
|
2012-06-08 21:49:12 +03:00
|
|
|
PA_HASHMAP_FOREACH(p, port->profiles, state2)
|
|
|
|
|
pa_tagstruct_puts(t, p->name);
|
2012-06-28 15:00:45 +02:00
|
|
|
|
2020-04-14 17:42:34 +02:00
|
|
|
if (c->version >= 27) {
|
2012-06-28 15:00:45 +02:00
|
|
|
pa_tagstruct_puts64(t, port->latency_offset);
|
2020-04-14 20:04:00 +02:00
|
|
|
if (c->version >= 34) {
|
2020-06-17 08:40:16 +03:00
|
|
|
pa_tagstruct_puts(t, port->availability_group);
|
2020-04-14 20:04:00 +02:00
|
|
|
pa_tagstruct_putu32(t, port->type);
|
|
|
|
|
}
|
2020-04-14 17:42:34 +02:00
|
|
|
}
|
2012-06-08 21:49:12 +03:00
|
|
|
}
|
2009-01-20 03:25:29 +01:00
|
|
|
}
|
|
|
|
|
|
2009-01-19 22:02:28 +01:00
|
|
|
static void module_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_module *module) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(t);
|
|
|
|
|
pa_assert(module);
|
|
|
|
|
|
2004-08-12 23:25:28 +00:00
|
|
|
pa_tagstruct_putu32(t, module->index);
|
|
|
|
|
pa_tagstruct_puts(t, module->name);
|
2004-09-19 23:12:41 +00:00
|
|
|
pa_tagstruct_puts(t, module->argument);
|
2009-01-15 20:49:12 +01:00
|
|
|
pa_tagstruct_putu32(t, (uint32_t) pa_module_get_n_used(module));
|
2009-01-19 22:02:28 +01:00
|
|
|
|
|
|
|
|
if (c->version < 15)
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_tagstruct_put_boolean(t, false); /* autoload is obsolete */
|
2009-01-19 22:02:28 +01:00
|
|
|
|
|
|
|
|
if (c->version >= 15)
|
|
|
|
|
pa_tagstruct_put_proplist(t, module->proplist);
|
2004-08-12 23:25:28 +00:00
|
|
|
}
|
|
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
static void sink_input_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sink_input *s) {
|
2007-11-21 22:55:28 +00:00
|
|
|
pa_sample_spec fixed_ss;
|
2008-06-20 22:32:41 +02:00
|
|
|
pa_usec_t sink_latency;
|
2009-04-13 22:50:24 +02:00
|
|
|
pa_cvolume v;
|
2013-06-27 19:28:09 +02:00
|
|
|
bool has_volume = false;
|
2007-11-21 22:55:28 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(t);
|
|
|
|
|
pa_sink_input_assert_ref(s);
|
|
|
|
|
|
2007-11-21 22:55:28 +00:00
|
|
|
fixup_sample_spec(c, &fixed_ss, &s->sample_spec);
|
|
|
|
|
|
2011-02-14 13:41:06 +02:00
|
|
|
has_volume = pa_sink_input_is_volume_readable(s);
|
|
|
|
|
if (has_volume)
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_sink_input_get_volume(s, &v, true);
|
2011-02-14 13:41:06 +02:00
|
|
|
else
|
|
|
|
|
pa_cvolume_reset(&v, fixed_ss.channels);
|
|
|
|
|
|
2004-08-16 19:55:02 +00:00
|
|
|
pa_tagstruct_putu32(t, s->index);
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_tagstruct_puts(t, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_MEDIA_NAME)));
|
2006-08-13 16:19:56 +00:00
|
|
|
pa_tagstruct_putu32(t, s->module ? s->module->index : PA_INVALID_INDEX);
|
2006-02-22 18:54:21 +00:00
|
|
|
pa_tagstruct_putu32(t, s->client ? s->client->index : PA_INVALID_INDEX);
|
2004-08-16 19:55:02 +00:00
|
|
|
pa_tagstruct_putu32(t, s->sink->index);
|
2007-11-21 22:55:28 +00:00
|
|
|
pa_tagstruct_put_sample_spec(t, &fixed_ss);
|
2006-01-27 16:25:31 +00:00
|
|
|
pa_tagstruct_put_channel_map(t, &s->channel_map);
|
2011-02-14 13:41:06 +02:00
|
|
|
pa_tagstruct_put_cvolume(t, &v);
|
2008-06-20 22:32:41 +02:00
|
|
|
pa_tagstruct_put_usec(t, pa_sink_input_get_latency(s, &sink_latency));
|
|
|
|
|
pa_tagstruct_put_usec(t, sink_latency);
|
2004-11-20 16:23:53 +00:00
|
|
|
pa_tagstruct_puts(t, pa_resample_method_to_string(pa_sink_input_get_resample_method(s)));
|
2006-01-27 16:25:31 +00:00
|
|
|
pa_tagstruct_puts(t, s->driver);
|
2007-10-28 19:13:50 +00:00
|
|
|
if (c->version >= 11)
|
2014-04-15 13:56:11 +03:00
|
|
|
pa_tagstruct_put_boolean(t, s->muted);
|
2008-05-15 23:34:41 +00:00
|
|
|
if (c->version >= 13)
|
|
|
|
|
pa_tagstruct_put_proplist(t, s->proplist);
|
2010-10-04 11:48:43 +01:00
|
|
|
if (c->version >= 19)
|
2018-06-26 16:25:57 +03:00
|
|
|
pa_tagstruct_put_boolean(t, s->state == PA_SINK_INPUT_CORKED);
|
2011-02-14 13:41:06 +02:00
|
|
|
if (c->version >= 20) {
|
|
|
|
|
pa_tagstruct_put_boolean(t, has_volume);
|
2011-03-27 23:00:26 +03:00
|
|
|
pa_tagstruct_put_boolean(t, s->volume_writable);
|
2011-02-14 13:41:06 +02:00
|
|
|
}
|
2011-03-08 23:31:59 +05:30
|
|
|
if (c->version >= 21)
|
|
|
|
|
pa_tagstruct_put_format_info(t, s->format);
|
2004-08-16 19:55:02 +00:00
|
|
|
}
|
|
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
static void source_output_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_source_output *s) {
|
2007-11-21 22:55:28 +00:00
|
|
|
pa_sample_spec fixed_ss;
|
2008-06-20 22:32:41 +02:00
|
|
|
pa_usec_t source_latency;
|
2011-05-17 22:31:10 +01:00
|
|
|
pa_cvolume v;
|
2013-06-27 19:28:09 +02:00
|
|
|
bool has_volume = false;
|
2007-11-21 22:55:28 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(t);
|
|
|
|
|
pa_source_output_assert_ref(s);
|
|
|
|
|
|
2007-11-21 22:55:28 +00:00
|
|
|
fixup_sample_spec(c, &fixed_ss, &s->sample_spec);
|
|
|
|
|
|
2011-05-17 22:31:10 +01:00
|
|
|
has_volume = pa_source_output_is_volume_readable(s);
|
|
|
|
|
if (has_volume)
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_source_output_get_volume(s, &v, true);
|
2011-05-17 22:31:10 +01:00
|
|
|
else
|
|
|
|
|
pa_cvolume_reset(&v, fixed_ss.channels);
|
|
|
|
|
|
2004-08-16 19:55:02 +00:00
|
|
|
pa_tagstruct_putu32(t, s->index);
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_tagstruct_puts(t, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_MEDIA_NAME)));
|
2006-08-13 19:55:17 +00:00
|
|
|
pa_tagstruct_putu32(t, s->module ? s->module->index : PA_INVALID_INDEX);
|
2006-02-22 18:54:21 +00:00
|
|
|
pa_tagstruct_putu32(t, s->client ? s->client->index : PA_INVALID_INDEX);
|
2004-08-16 19:55:02 +00:00
|
|
|
pa_tagstruct_putu32(t, s->source->index);
|
2007-11-21 22:55:28 +00:00
|
|
|
pa_tagstruct_put_sample_spec(t, &fixed_ss);
|
2006-01-27 16:25:31 +00:00
|
|
|
pa_tagstruct_put_channel_map(t, &s->channel_map);
|
2008-06-20 22:32:41 +02:00
|
|
|
pa_tagstruct_put_usec(t, pa_source_output_get_latency(s, &source_latency));
|
|
|
|
|
pa_tagstruct_put_usec(t, source_latency);
|
2004-11-20 16:23:53 +00:00
|
|
|
pa_tagstruct_puts(t, pa_resample_method_to_string(pa_source_output_get_resample_method(s)));
|
2006-01-27 16:25:31 +00:00
|
|
|
pa_tagstruct_puts(t, s->driver);
|
2008-05-15 23:34:41 +00:00
|
|
|
if (c->version >= 13)
|
|
|
|
|
pa_tagstruct_put_proplist(t, s->proplist);
|
2010-10-04 11:48:43 +01:00
|
|
|
if (c->version >= 19)
|
2018-06-26 16:25:57 +03:00
|
|
|
pa_tagstruct_put_boolean(t, s->state == PA_SOURCE_OUTPUT_CORKED);
|
2011-05-17 22:31:10 +01:00
|
|
|
if (c->version >= 22) {
|
|
|
|
|
pa_tagstruct_put_cvolume(t, &v);
|
2014-04-15 13:56:11 +03:00
|
|
|
pa_tagstruct_put_boolean(t, s->muted);
|
2011-05-17 22:31:10 +01:00
|
|
|
pa_tagstruct_put_boolean(t, has_volume);
|
|
|
|
|
pa_tagstruct_put_boolean(t, s->volume_writable);
|
2011-05-16 22:23:24 +01:00
|
|
|
pa_tagstruct_put_format_info(t, s->format);
|
2011-05-17 22:31:10 +01:00
|
|
|
}
|
2004-08-16 19:55:02 +00:00
|
|
|
}
|
|
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
static void scache_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_scache_entry *e) {
|
2007-11-21 22:55:28 +00:00
|
|
|
pa_sample_spec fixed_ss;
|
2009-01-27 03:05:40 +01:00
|
|
|
pa_cvolume v;
|
2007-11-21 22:55:28 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(t);
|
|
|
|
|
pa_assert(e);
|
|
|
|
|
|
2008-06-14 21:33:21 +00:00
|
|
|
if (e->memchunk.memblock)
|
|
|
|
|
fixup_sample_spec(c, &fixed_ss, &e->sample_spec);
|
|
|
|
|
else
|
|
|
|
|
memset(&fixed_ss, 0, sizeof(fixed_ss));
|
2007-11-21 22:55:28 +00:00
|
|
|
|
2004-08-19 23:14:59 +00:00
|
|
|
pa_tagstruct_putu32(t, e->index);
|
|
|
|
|
pa_tagstruct_puts(t, e->name);
|
2009-01-27 03:05:40 +01:00
|
|
|
|
|
|
|
|
if (e->volume_is_set)
|
|
|
|
|
v = e->volume;
|
|
|
|
|
else
|
|
|
|
|
pa_cvolume_init(&v);
|
|
|
|
|
|
|
|
|
|
pa_tagstruct_put_cvolume(t, &v);
|
2008-06-17 18:29:00 +00:00
|
|
|
pa_tagstruct_put_usec(t, e->memchunk.memblock ? pa_bytes_to_usec(e->memchunk.length, &e->sample_spec) : 0);
|
2007-11-21 22:55:28 +00:00
|
|
|
pa_tagstruct_put_sample_spec(t, &fixed_ss);
|
2006-01-27 16:25:31 +00:00
|
|
|
pa_tagstruct_put_channel_map(t, &e->channel_map);
|
2008-08-19 22:39:54 +02:00
|
|
|
pa_tagstruct_putu32(t, (uint32_t) e->memchunk.length);
|
2004-09-15 19:16:57 +00:00
|
|
|
pa_tagstruct_put_boolean(t, e->lazy);
|
|
|
|
|
pa_tagstruct_puts(t, e->filename);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
|
|
|
|
if (c->version >= 13)
|
|
|
|
|
pa_tagstruct_put_proplist(t, e->proplist);
|
2004-08-19 23:14:59 +00:00
|
|
|
}
|
|
|
|
|
|
2008-08-09 16:20:29 +02:00
|
|
|
static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
2006-01-11 01:17:39 +00:00
|
|
|
uint32_t idx;
|
|
|
|
|
pa_sink *sink = NULL;
|
|
|
|
|
pa_source *source = NULL;
|
|
|
|
|
pa_client *client = NULL;
|
2009-01-20 03:25:29 +01:00
|
|
|
pa_card *card = NULL;
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_module *module = NULL;
|
|
|
|
|
pa_sink_input *si = NULL;
|
|
|
|
|
pa_source_output *so = NULL;
|
|
|
|
|
pa_scache_entry *sce = NULL;
|
2008-09-01 16:00:08 +03:00
|
|
|
const char *name = NULL;
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_tagstruct *reply;
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_assert_ref(c);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(t);
|
2004-08-05 19:53:57 +00:00
|
|
|
|
2006-01-11 01:17:39 +00:00
|
|
|
if (pa_tagstruct_getu32(t, &idx) < 0 ||
|
2004-08-16 19:55:02 +00:00
|
|
|
(command != PA_COMMAND_GET_CLIENT_INFO &&
|
|
|
|
|
command != PA_COMMAND_GET_MODULE_INFO &&
|
|
|
|
|
command != PA_COMMAND_GET_SINK_INPUT_INFO &&
|
|
|
|
|
command != PA_COMMAND_GET_SOURCE_OUTPUT_INFO &&
|
|
|
|
|
pa_tagstruct_gets(t, &name) < 0) ||
|
2004-08-05 19:53:57 +00:00
|
|
|
!pa_tagstruct_eof(t)) {
|
|
|
|
|
protocol_error(c);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2006-02-22 18:54:21 +00:00
|
|
|
|
|
|
|
|
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
|
2009-08-29 03:57:53 +02:00
|
|
|
CHECK_VALIDITY(c->pstream, !name ||
|
|
|
|
|
(command == PA_COMMAND_GET_SINK_INFO &&
|
|
|
|
|
pa_namereg_is_valid_name_or_wildcard(name, PA_NAMEREG_SINK)) ||
|
|
|
|
|
(command == PA_COMMAND_GET_SOURCE_INFO &&
|
|
|
|
|
pa_namereg_is_valid_name_or_wildcard(name, PA_NAMEREG_SOURCE)) ||
|
|
|
|
|
pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID);
|
2011-07-11 20:52:41 -07:00
|
|
|
CHECK_VALIDITY(c->pstream, command == PA_COMMAND_GET_SINK_INFO ||
|
|
|
|
|
command == PA_COMMAND_GET_SOURCE_INFO ||
|
|
|
|
|
(idx != PA_INVALID_INDEX || name), tag, PA_ERR_INVALID);
|
2008-08-18 17:48:04 +02:00
|
|
|
CHECK_VALIDITY(c->pstream, idx == PA_INVALID_INDEX || !name, tag, PA_ERR_INVALID);
|
2004-08-05 19:53:57 +00:00
|
|
|
|
|
|
|
|
if (command == PA_COMMAND_GET_SINK_INFO) {
|
2006-02-22 18:54:21 +00:00
|
|
|
if (idx != PA_INVALID_INDEX)
|
2006-01-11 01:17:39 +00:00
|
|
|
sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
|
2004-08-05 19:53:57 +00:00
|
|
|
else
|
2009-01-15 20:07:13 +01:00
|
|
|
sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK);
|
2004-08-12 23:25:28 +00:00
|
|
|
} else if (command == PA_COMMAND_GET_SOURCE_INFO) {
|
2006-02-22 18:54:21 +00:00
|
|
|
if (idx != PA_INVALID_INDEX)
|
2006-01-11 01:17:39 +00:00
|
|
|
source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
|
2004-08-05 19:53:57 +00:00
|
|
|
else
|
2009-01-15 20:07:13 +01:00
|
|
|
source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE);
|
2009-01-20 03:25:29 +01:00
|
|
|
} else if (command == PA_COMMAND_GET_CARD_INFO) {
|
|
|
|
|
if (idx != PA_INVALID_INDEX)
|
|
|
|
|
card = pa_idxset_get_by_index(c->protocol->core->cards, idx);
|
|
|
|
|
else
|
|
|
|
|
card = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_CARD);
|
2004-08-12 23:25:28 +00:00
|
|
|
} else if (command == PA_COMMAND_GET_CLIENT_INFO)
|
2006-01-11 01:17:39 +00:00
|
|
|
client = pa_idxset_get_by_index(c->protocol->core->clients, idx);
|
2007-01-04 13:43:45 +00:00
|
|
|
else if (command == PA_COMMAND_GET_MODULE_INFO)
|
2006-01-11 01:17:39 +00:00
|
|
|
module = pa_idxset_get_by_index(c->protocol->core->modules, idx);
|
2004-08-16 19:55:02 +00:00
|
|
|
else if (command == PA_COMMAND_GET_SINK_INPUT_INFO)
|
2006-01-11 01:17:39 +00:00
|
|
|
si = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx);
|
2004-08-19 23:14:59 +00:00
|
|
|
else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO)
|
2006-01-11 01:17:39 +00:00
|
|
|
so = pa_idxset_get_by_index(c->protocol->core->source_outputs, idx);
|
2004-08-19 23:14:59 +00:00
|
|
|
else {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(command == PA_COMMAND_GET_SAMPLE_INFO);
|
2006-02-22 18:54:21 +00:00
|
|
|
if (idx != PA_INVALID_INDEX)
|
2006-01-11 01:17:39 +00:00
|
|
|
sce = pa_idxset_get_by_index(c->protocol->core->scache, idx);
|
2004-08-19 23:14:59 +00:00
|
|
|
else
|
2009-01-15 20:07:13 +01:00
|
|
|
sce = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SAMPLE);
|
2004-08-05 19:53:57 +00:00
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2009-01-20 03:25:29 +01:00
|
|
|
if (!sink && !source && !client && !card && !module && !si && !so && !sce) {
|
2006-02-20 04:05:16 +00:00
|
|
|
pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
|
2004-08-05 19:53:57 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-22 18:54:21 +00:00
|
|
|
reply = reply_new(tag);
|
2004-08-05 19:53:57 +00:00
|
|
|
if (sink)
|
2007-11-21 22:55:28 +00:00
|
|
|
sink_fill_tagstruct(c, reply, sink);
|
2004-08-12 23:25:28 +00:00
|
|
|
else if (source)
|
2007-11-21 22:55:28 +00:00
|
|
|
source_fill_tagstruct(c, reply, source);
|
2004-08-12 23:25:28 +00:00
|
|
|
else if (client)
|
2008-05-15 23:34:41 +00:00
|
|
|
client_fill_tagstruct(c, reply, client);
|
2009-02-17 20:18:56 +02:00
|
|
|
else if (card)
|
2009-01-20 03:25:29 +01:00
|
|
|
card_fill_tagstruct(c, reply, card);
|
2004-08-16 19:55:02 +00:00
|
|
|
else if (module)
|
2009-01-19 22:02:28 +01:00
|
|
|
module_fill_tagstruct(c, reply, module);
|
2004-08-16 19:55:02 +00:00
|
|
|
else if (si)
|
2007-10-28 19:13:50 +00:00
|
|
|
sink_input_fill_tagstruct(c, reply, si);
|
2004-08-19 23:14:59 +00:00
|
|
|
else if (so)
|
2007-11-21 22:55:28 +00:00
|
|
|
source_output_fill_tagstruct(c, reply, so);
|
2004-08-19 23:14:59 +00:00
|
|
|
else
|
2007-11-21 22:55:28 +00:00
|
|
|
scache_fill_tagstruct(c, reply, sce);
|
2004-08-05 19:53:57 +00:00
|
|
|
pa_pstream_send_tagstruct(c->pstream, reply);
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-09 16:20:29 +02:00
|
|
|
static void command_get_info_list(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_idxset *i;
|
|
|
|
|
uint32_t idx;
|
2004-08-05 19:53:57 +00:00
|
|
|
void *p;
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_tagstruct *reply;
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_assert_ref(c);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(t);
|
2004-08-05 19:53:57 +00:00
|
|
|
|
|
|
|
|
if (!pa_tagstruct_eof(t)) {
|
|
|
|
|
protocol_error(c);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-22 18:54:21 +00:00
|
|
|
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
|
|
|
|
|
|
|
|
|
|
reply = reply_new(tag);
|
2004-08-05 19:53:57 +00:00
|
|
|
|
|
|
|
|
if (command == PA_COMMAND_GET_SINK_INFO_LIST)
|
|
|
|
|
i = c->protocol->core->sinks;
|
2004-08-12 23:25:28 +00:00
|
|
|
else if (command == PA_COMMAND_GET_SOURCE_INFO_LIST)
|
2004-08-05 19:53:57 +00:00
|
|
|
i = c->protocol->core->sources;
|
2004-08-12 23:25:28 +00:00
|
|
|
else if (command == PA_COMMAND_GET_CLIENT_INFO_LIST)
|
|
|
|
|
i = c->protocol->core->clients;
|
2009-01-20 03:25:29 +01:00
|
|
|
else if (command == PA_COMMAND_GET_CARD_INFO_LIST)
|
|
|
|
|
i = c->protocol->core->cards;
|
2004-08-16 19:55:02 +00:00
|
|
|
else if (command == PA_COMMAND_GET_MODULE_INFO_LIST)
|
2004-08-12 23:25:28 +00:00
|
|
|
i = c->protocol->core->modules;
|
2004-08-16 19:55:02 +00:00
|
|
|
else if (command == PA_COMMAND_GET_SINK_INPUT_INFO_LIST)
|
|
|
|
|
i = c->protocol->core->sink_inputs;
|
2004-08-19 23:14:59 +00:00
|
|
|
else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST)
|
2004-08-16 19:55:02 +00:00
|
|
|
i = c->protocol->core->source_outputs;
|
2004-08-19 23:14:59 +00:00
|
|
|
else {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(command == PA_COMMAND_GET_SAMPLE_INFO_LIST);
|
2004-08-19 23:14:59 +00:00
|
|
|
i = c->protocol->core->scache;
|
2004-08-05 19:53:57 +00:00
|
|
|
}
|
2004-08-20 10:54:31 +00:00
|
|
|
|
|
|
|
|
if (i) {
|
2012-05-29 17:58:12 +05:30
|
|
|
PA_IDXSET_FOREACH(p, i, idx) {
|
2014-02-17 16:32:40 +05:30
|
|
|
if (command == PA_COMMAND_GET_SINK_INFO_LIST)
|
|
|
|
|
sink_fill_tagstruct(c, reply, p);
|
|
|
|
|
else if (command == PA_COMMAND_GET_SOURCE_INFO_LIST)
|
|
|
|
|
source_fill_tagstruct(c, reply, p);
|
|
|
|
|
else if (command == PA_COMMAND_GET_CLIENT_INFO_LIST)
|
2008-05-15 23:34:41 +00:00
|
|
|
client_fill_tagstruct(c, reply, p);
|
2009-01-20 03:25:29 +01:00
|
|
|
else if (command == PA_COMMAND_GET_CARD_INFO_LIST)
|
|
|
|
|
card_fill_tagstruct(c, reply, p);
|
2004-08-20 10:54:31 +00:00
|
|
|
else if (command == PA_COMMAND_GET_MODULE_INFO_LIST)
|
2009-01-19 22:02:28 +01:00
|
|
|
module_fill_tagstruct(c, reply, p);
|
2004-08-20 10:54:31 +00:00
|
|
|
else if (command == PA_COMMAND_GET_SINK_INPUT_INFO_LIST)
|
2007-10-28 19:13:50 +00:00
|
|
|
sink_input_fill_tagstruct(c, reply, p);
|
2007-01-04 13:43:45 +00:00
|
|
|
else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST)
|
2007-11-21 22:55:28 +00:00
|
|
|
source_output_fill_tagstruct(c, reply, p);
|
2004-08-20 10:54:31 +00:00
|
|
|
else {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(command == PA_COMMAND_GET_SAMPLE_INFO_LIST);
|
2007-11-21 22:55:28 +00:00
|
|
|
scache_fill_tagstruct(c, reply, p);
|
2004-08-20 10:54:31 +00:00
|
|
|
}
|
2004-08-05 19:53:57 +00:00
|
|
|
}
|
2004-08-20 10:54:31 +00:00
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-08-05 19:53:57 +00:00
|
|
|
pa_pstream_send_tagstruct(c->pstream, reply);
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-09 16:20:29 +02:00
|
|
|
static void command_get_server_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_tagstruct *reply;
|
2007-11-21 22:55:28 +00:00
|
|
|
pa_sample_spec fixed_ss;
|
2009-04-29 01:56:02 +02:00
|
|
|
char *h, *u;
|
improve default sink/source handling
Currently the default sink policy is simple: either the user has
configured it explicitly, in which case we always use that as the
default, or we pick the sink with the highest priority. The sink
priorities are currently static, so there's no need to worry about
updating the default sink when sink priorities change.
I intend to make things a bit more complex: if the active port of a sink
is unavailable, the sink should not be the default sink, and I also want
to make sink priorities dependent on the active port, so changing the
port should cause re-evaluation of which sink to choose as the default.
Currently the default sink choice is done only when someone calls
pa_namereg_get_default_sink(), and change notifications are only sent
when a sink is created or destroyed. That makes it hard to add new rules
to the default sink selection policy.
This patch moves the default sink selection to
pa_core_update_default_sink(), which is called whenever something
happens that can affect the default sink choice. That function needs to
know the previous choice in order to send change notifications as
appropriate, but previously pa_core.default_sink was only set when the
user had configured it explicitly. Now pa_core.default_sink is always
set (unless there are no sinks at all), so pa_core_update_default_sink()
can use that to get the previous choice. The user configuration is saved
in a new variable, pa_core.configured_default_sink.
pa_namereg_get_default_sink() is now unnecessary, because
pa_core.default_sink can be used directly to get the
currently-considered-best sink. pa_namereg_set_default_sink() is
replaced by pa_core_set_configured_default_sink().
I haven't confirmed it, but I expect that this patch will fix problems
in the D-Bus protocol related to default sink handling. The D-Bus
protocol used to get confused when the current default sink gets
removed. It would incorrectly think that if there's no explicitly
configured default sink, then there's no default sink at all. Even
worse, when the D-Bus thinks that there's no default sink, it concludes
that there are no sinks at all, which made it impossible to configure
the default sink via the D-Bus interface. Now that pa_core.default_sink
is always set, except when there really aren't any sinks, the D-Bus
protocol should behave correctly.
BugLink: https://bugs.freedesktop.org/show_bug.cgi?id=99425
2017-02-16 12:09:38 +02:00
|
|
|
pa_core *core;
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_assert_ref(c);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(t);
|
2004-08-10 13:00:12 +00:00
|
|
|
|
|
|
|
|
if (!pa_tagstruct_eof(t)) {
|
|
|
|
|
protocol_error(c);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-02-22 18:54:21 +00:00
|
|
|
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
|
2004-08-10 13:00:12 +00:00
|
|
|
|
2006-02-22 18:54:21 +00:00
|
|
|
reply = reply_new(tag);
|
2004-08-10 13:00:12 +00:00
|
|
|
pa_tagstruct_puts(reply, PACKAGE_NAME);
|
|
|
|
|
pa_tagstruct_puts(reply, PACKAGE_VERSION);
|
2009-04-29 01:56:02 +02:00
|
|
|
|
|
|
|
|
u = pa_get_user_name_malloc();
|
|
|
|
|
pa_tagstruct_puts(reply, u);
|
|
|
|
|
pa_xfree(u);
|
|
|
|
|
|
|
|
|
|
h = pa_get_host_name_malloc();
|
|
|
|
|
pa_tagstruct_puts(reply, h);
|
|
|
|
|
pa_xfree(h);
|
2007-11-21 22:55:28 +00:00
|
|
|
|
improve default sink/source handling
Currently the default sink policy is simple: either the user has
configured it explicitly, in which case we always use that as the
default, or we pick the sink with the highest priority. The sink
priorities are currently static, so there's no need to worry about
updating the default sink when sink priorities change.
I intend to make things a bit more complex: if the active port of a sink
is unavailable, the sink should not be the default sink, and I also want
to make sink priorities dependent on the active port, so changing the
port should cause re-evaluation of which sink to choose as the default.
Currently the default sink choice is done only when someone calls
pa_namereg_get_default_sink(), and change notifications are only sent
when a sink is created or destroyed. That makes it hard to add new rules
to the default sink selection policy.
This patch moves the default sink selection to
pa_core_update_default_sink(), which is called whenever something
happens that can affect the default sink choice. That function needs to
know the previous choice in order to send change notifications as
appropriate, but previously pa_core.default_sink was only set when the
user had configured it explicitly. Now pa_core.default_sink is always
set (unless there are no sinks at all), so pa_core_update_default_sink()
can use that to get the previous choice. The user configuration is saved
in a new variable, pa_core.configured_default_sink.
pa_namereg_get_default_sink() is now unnecessary, because
pa_core.default_sink can be used directly to get the
currently-considered-best sink. pa_namereg_set_default_sink() is
replaced by pa_core_set_configured_default_sink().
I haven't confirmed it, but I expect that this patch will fix problems
in the D-Bus protocol related to default sink handling. The D-Bus
protocol used to get confused when the current default sink gets
removed. It would incorrectly think that if there's no explicitly
configured default sink, then there's no default sink at all. Even
worse, when the D-Bus thinks that there's no default sink, it concludes
that there are no sinks at all, which made it impossible to configure
the default sink via the D-Bus interface. Now that pa_core.default_sink
is always set, except when there really aren't any sinks, the D-Bus
protocol should behave correctly.
BugLink: https://bugs.freedesktop.org/show_bug.cgi?id=99425
2017-02-16 12:09:38 +02:00
|
|
|
core = c->protocol->core;
|
|
|
|
|
|
|
|
|
|
fixup_sample_spec(c, &fixed_ss, &core->default_sample_spec);
|
2007-11-21 22:55:28 +00:00
|
|
|
pa_tagstruct_put_sample_spec(reply, &fixed_ss);
|
2004-09-07 14:58:42 +00:00
|
|
|
|
improve default sink/source handling
Currently the default sink policy is simple: either the user has
configured it explicitly, in which case we always use that as the
default, or we pick the sink with the highest priority. The sink
priorities are currently static, so there's no need to worry about
updating the default sink when sink priorities change.
I intend to make things a bit more complex: if the active port of a sink
is unavailable, the sink should not be the default sink, and I also want
to make sink priorities dependent on the active port, so changing the
port should cause re-evaluation of which sink to choose as the default.
Currently the default sink choice is done only when someone calls
pa_namereg_get_default_sink(), and change notifications are only sent
when a sink is created or destroyed. That makes it hard to add new rules
to the default sink selection policy.
This patch moves the default sink selection to
pa_core_update_default_sink(), which is called whenever something
happens that can affect the default sink choice. That function needs to
know the previous choice in order to send change notifications as
appropriate, but previously pa_core.default_sink was only set when the
user had configured it explicitly. Now pa_core.default_sink is always
set (unless there are no sinks at all), so pa_core_update_default_sink()
can use that to get the previous choice. The user configuration is saved
in a new variable, pa_core.configured_default_sink.
pa_namereg_get_default_sink() is now unnecessary, because
pa_core.default_sink can be used directly to get the
currently-considered-best sink. pa_namereg_set_default_sink() is
replaced by pa_core_set_configured_default_sink().
I haven't confirmed it, but I expect that this patch will fix problems
in the D-Bus protocol related to default sink handling. The D-Bus
protocol used to get confused when the current default sink gets
removed. It would incorrectly think that if there's no explicitly
configured default sink, then there's no default sink at all. Even
worse, when the D-Bus thinks that there's no default sink, it concludes
that there are no sinks at all, which made it impossible to configure
the default sink via the D-Bus interface. Now that pa_core.default_sink
is always set, except when there really aren't any sinks, the D-Bus
protocol should behave correctly.
BugLink: https://bugs.freedesktop.org/show_bug.cgi?id=99425
2017-02-16 12:09:38 +02:00
|
|
|
pa_tagstruct_puts(reply, core->default_sink ? core->default_sink->name : NULL);
|
|
|
|
|
pa_tagstruct_puts(reply, core->default_source ? core->default_source->name : NULL);
|
2004-12-12 22:58:53 +00:00
|
|
|
|
|
|
|
|
pa_tagstruct_putu32(reply, c->protocol->core->cookie);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2009-02-21 16:32:42 +01:00
|
|
|
if (c->version >= 15)
|
improve default sink/source handling
Currently the default sink policy is simple: either the user has
configured it explicitly, in which case we always use that as the
default, or we pick the sink with the highest priority. The sink
priorities are currently static, so there's no need to worry about
updating the default sink when sink priorities change.
I intend to make things a bit more complex: if the active port of a sink
is unavailable, the sink should not be the default sink, and I also want
to make sink priorities dependent on the active port, so changing the
port should cause re-evaluation of which sink to choose as the default.
Currently the default sink choice is done only when someone calls
pa_namereg_get_default_sink(), and change notifications are only sent
when a sink is created or destroyed. That makes it hard to add new rules
to the default sink selection policy.
This patch moves the default sink selection to
pa_core_update_default_sink(), which is called whenever something
happens that can affect the default sink choice. That function needs to
know the previous choice in order to send change notifications as
appropriate, but previously pa_core.default_sink was only set when the
user had configured it explicitly. Now pa_core.default_sink is always
set (unless there are no sinks at all), so pa_core_update_default_sink()
can use that to get the previous choice. The user configuration is saved
in a new variable, pa_core.configured_default_sink.
pa_namereg_get_default_sink() is now unnecessary, because
pa_core.default_sink can be used directly to get the
currently-considered-best sink. pa_namereg_set_default_sink() is
replaced by pa_core_set_configured_default_sink().
I haven't confirmed it, but I expect that this patch will fix problems
in the D-Bus protocol related to default sink handling. The D-Bus
protocol used to get confused when the current default sink gets
removed. It would incorrectly think that if there's no explicitly
configured default sink, then there's no default sink at all. Even
worse, when the D-Bus thinks that there's no default sink, it concludes
that there are no sinks at all, which made it impossible to configure
the default sink via the D-Bus interface. Now that pa_core.default_sink
is always set, except when there really aren't any sinks, the D-Bus
protocol should behave correctly.
BugLink: https://bugs.freedesktop.org/show_bug.cgi?id=99425
2017-02-16 12:09:38 +02:00
|
|
|
pa_tagstruct_put_channel_map(reply, &core->default_channel_map);
|
2009-02-21 16:32:42 +01:00
|
|
|
|
2004-08-10 13:00:12 +00:00
|
|
|
pa_pstream_send_tagstruct(c->pstream, reply);
|
|
|
|
|
}
|
|
|
|
|
|
2006-01-27 16:25:31 +00:00
|
|
|
static void subscription_cb(pa_core *core, pa_subscription_event_type_t e, uint32_t idx, void *userdata) {
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_tagstruct *t;
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_assert_ref(c);
|
2004-08-11 15:11:26 +00:00
|
|
|
|
2014-10-23 16:09:45 +02:00
|
|
|
t = pa_tagstruct_new();
|
2004-08-11 15:11:26 +00:00
|
|
|
pa_tagstruct_putu32(t, PA_COMMAND_SUBSCRIBE_EVENT);
|
|
|
|
|
pa_tagstruct_putu32(t, (uint32_t) -1);
|
|
|
|
|
pa_tagstruct_putu32(t, e);
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_tagstruct_putu32(t, idx);
|
2004-08-11 15:11:26 +00:00
|
|
|
pa_pstream_send_tagstruct(c->pstream, t);
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-09 16:20:29 +02:00
|
|
|
static void command_subscribe(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
2006-01-27 16:25:31 +00:00
|
|
|
pa_subscription_mask_t m;
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_assert_ref(c);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(t);
|
2004-08-11 15:11:26 +00:00
|
|
|
|
|
|
|
|
if (pa_tagstruct_getu32(t, &m) < 0 ||
|
|
|
|
|
!pa_tagstruct_eof(t)) {
|
|
|
|
|
protocol_error(c);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-22 18:54:21 +00:00
|
|
|
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
|
|
|
|
|
CHECK_VALIDITY(c->pstream, (m & ~PA_SUBSCRIPTION_MASK_ALL) == 0, tag, PA_ERR_INVALID);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-08-11 15:11:26 +00:00
|
|
|
if (c->subscription)
|
|
|
|
|
pa_subscription_free(c->subscription);
|
|
|
|
|
|
|
|
|
|
if (m != 0) {
|
|
|
|
|
c->subscription = pa_subscription_new(c->protocol->core, m, subscription_cb, c);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(c->subscription);
|
2004-08-11 15:11:26 +00:00
|
|
|
} else
|
|
|
|
|
c->subscription = NULL;
|
|
|
|
|
|
|
|
|
|
pa_pstream_send_simple_ack(c->pstream, tag);
|
2004-08-15 13:15:51 +00:00
|
|
|
}
|
|
|
|
|
|
2006-02-22 18:54:21 +00:00
|
|
|
static void command_set_volume(
|
2008-08-09 16:20:29 +02:00
|
|
|
pa_pdispatch *pd,
|
2006-02-22 18:54:21 +00:00
|
|
|
uint32_t command,
|
|
|
|
|
uint32_t tag,
|
|
|
|
|
pa_tagstruct *t,
|
|
|
|
|
void *userdata) {
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
2006-01-27 16:25:31 +00:00
|
|
|
uint32_t idx;
|
|
|
|
|
pa_cvolume volume;
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_sink *sink = NULL;
|
2006-02-22 14:11:23 +00:00
|
|
|
pa_source *source = NULL;
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_sink_input *si = NULL;
|
2011-05-17 22:31:10 +01:00
|
|
|
pa_source_output *so = NULL;
|
2004-08-15 13:15:51 +00:00
|
|
|
const char *name = NULL;
|
2009-08-14 19:31:05 +02:00
|
|
|
const char *client_name;
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_assert_ref(c);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(t);
|
2004-08-15 13:15:51 +00:00
|
|
|
|
2006-01-11 01:17:39 +00:00
|
|
|
if (pa_tagstruct_getu32(t, &idx) < 0 ||
|
2004-08-15 13:15:51 +00:00
|
|
|
(command == PA_COMMAND_SET_SINK_VOLUME && pa_tagstruct_gets(t, &name) < 0) ||
|
2006-02-22 14:11:23 +00:00
|
|
|
(command == PA_COMMAND_SET_SOURCE_VOLUME && pa_tagstruct_gets(t, &name) < 0) ||
|
2006-01-27 16:25:31 +00:00
|
|
|
pa_tagstruct_get_cvolume(t, &volume) ||
|
2004-08-15 13:15:51 +00:00
|
|
|
!pa_tagstruct_eof(t)) {
|
|
|
|
|
protocol_error(c);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-02-22 18:54:21 +00:00
|
|
|
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
|
2009-08-29 03:57:53 +02:00
|
|
|
CHECK_VALIDITY(c->pstream, !name || pa_namereg_is_valid_name_or_wildcard(name, command == PA_COMMAND_SET_SINK_VOLUME ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE), tag, PA_ERR_INVALID);
|
2013-11-19 11:59:39 -09:00
|
|
|
CHECK_VALIDITY(c->pstream, (idx != PA_INVALID_INDEX) ^ (name != NULL), tag, PA_ERR_INVALID);
|
2006-04-23 21:01:44 +00:00
|
|
|
CHECK_VALIDITY(c->pstream, pa_cvolume_valid(&volume), tag, PA_ERR_INVALID);
|
2004-08-15 13:15:51 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
switch (command) {
|
|
|
|
|
|
|
|
|
|
case PA_COMMAND_SET_SINK_VOLUME:
|
|
|
|
|
if (idx != PA_INVALID_INDEX)
|
|
|
|
|
sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
|
|
|
|
|
else
|
2009-01-15 20:07:13 +01:00
|
|
|
sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK);
|
2007-10-28 19:13:50 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PA_COMMAND_SET_SOURCE_VOLUME:
|
|
|
|
|
if (idx != PA_INVALID_INDEX)
|
|
|
|
|
source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
|
|
|
|
|
else
|
2009-01-15 20:07:13 +01:00
|
|
|
source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE);
|
2007-10-28 19:13:50 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PA_COMMAND_SET_SINK_INPUT_VOLUME:
|
|
|
|
|
si = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx);
|
|
|
|
|
break;
|
|
|
|
|
|
2011-05-17 22:31:10 +01:00
|
|
|
case PA_COMMAND_SET_SOURCE_OUTPUT_VOLUME:
|
|
|
|
|
so = pa_idxset_get_by_index(c->protocol->core->source_outputs, idx);
|
|
|
|
|
break;
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
default:
|
|
|
|
|
pa_assert_not_reached();
|
2004-08-15 13:15:51 +00:00
|
|
|
}
|
|
|
|
|
|
2011-05-17 22:31:10 +01:00
|
|
|
CHECK_VALIDITY(c->pstream, si || so || sink || source, tag, PA_ERR_NOENTITY);
|
2004-08-15 13:15:51 +00:00
|
|
|
|
2009-08-14 19:31:05 +02:00
|
|
|
client_name = pa_strnull(pa_proplist_gets(c->client->proplist, PA_PROP_APPLICATION_PROCESS_BINARY));
|
|
|
|
|
|
|
|
|
|
if (sink) {
|
2009-08-31 21:40:59 +02:00
|
|
|
CHECK_VALIDITY(c->pstream, volume.channels == 1 || pa_cvolume_compatible(&volume, &sink->sample_spec), tag, PA_ERR_INVALID);
|
2009-08-29 06:11:02 +02:00
|
|
|
|
2009-08-15 00:54:51 +02:00
|
|
|
pa_log_debug("Client %s changes volume of sink %s.", client_name, sink->name);
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_sink_set_volume(sink, &volume, true, true);
|
2009-08-14 19:31:05 +02:00
|
|
|
} else if (source) {
|
2009-08-31 21:40:59 +02:00
|
|
|
CHECK_VALIDITY(c->pstream, volume.channels == 1 || pa_cvolume_compatible(&volume, &source->sample_spec), tag, PA_ERR_INVALID);
|
2009-08-29 06:11:02 +02:00
|
|
|
|
2009-08-27 00:05:54 +02:00
|
|
|
pa_log_debug("Client %s changes volume of source %s.", client_name, source->name);
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_source_set_volume(source, &volume, true, true);
|
2009-08-14 19:31:05 +02:00
|
|
|
} else if (si) {
|
2011-03-27 23:00:26 +03:00
|
|
|
CHECK_VALIDITY(c->pstream, si->volume_writable, tag, PA_ERR_BADSTATE);
|
2009-08-31 21:40:59 +02:00
|
|
|
CHECK_VALIDITY(c->pstream, volume.channels == 1 || pa_cvolume_compatible(&volume, &si->sample_spec), tag, PA_ERR_INVALID);
|
2009-08-29 06:11:02 +02:00
|
|
|
|
2009-08-27 00:05:54 +02:00
|
|
|
pa_log_debug("Client %s changes volume of sink input %s.",
|
2009-08-14 19:31:05 +02:00
|
|
|
client_name,
|
|
|
|
|
pa_strnull(pa_proplist_gets(si->proplist, PA_PROP_MEDIA_NAME)));
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_sink_input_set_volume(si, &volume, true, true);
|
2011-05-17 22:31:10 +01:00
|
|
|
} else if (so) {
|
2014-02-04 21:28:44 +02:00
|
|
|
CHECK_VALIDITY(c->pstream, so->volume_writable, tag, PA_ERR_BADSTATE);
|
2011-05-17 22:31:10 +01:00
|
|
|
CHECK_VALIDITY(c->pstream, volume.channels == 1 || pa_cvolume_compatible(&volume, &so->sample_spec), tag, PA_ERR_INVALID);
|
|
|
|
|
|
|
|
|
|
pa_log_debug("Client %s changes volume of source output %s.",
|
|
|
|
|
client_name,
|
|
|
|
|
pa_strnull(pa_proplist_gets(so->proplist, PA_PROP_MEDIA_NAME)));
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_source_output_set_volume(so, &volume, true, true);
|
2009-08-14 19:31:05 +02:00
|
|
|
}
|
2004-08-15 13:15:51 +00:00
|
|
|
|
|
|
|
|
pa_pstream_send_simple_ack(c->pstream, tag);
|
2004-08-11 15:11:26 +00:00
|
|
|
}
|
|
|
|
|
|
2006-02-23 12:04:31 +00:00
|
|
|
static void command_set_mute(
|
2008-08-09 16:20:29 +02:00
|
|
|
pa_pdispatch *pd,
|
2006-02-23 12:04:31 +00:00
|
|
|
uint32_t command,
|
|
|
|
|
uint32_t tag,
|
|
|
|
|
pa_tagstruct *t,
|
|
|
|
|
void *userdata) {
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
2006-02-23 12:04:31 +00:00
|
|
|
uint32_t idx;
|
2013-06-27 19:28:09 +02:00
|
|
|
bool mute;
|
2006-02-23 12:04:31 +00:00
|
|
|
pa_sink *sink = NULL;
|
|
|
|
|
pa_source *source = NULL;
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_sink_input *si = NULL;
|
2011-05-17 22:31:10 +01:00
|
|
|
pa_source_output *so = NULL;
|
2009-08-27 00:05:54 +02:00
|
|
|
const char *name = NULL, *client_name;
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_assert_ref(c);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(t);
|
2006-02-23 12:04:31 +00:00
|
|
|
|
|
|
|
|
if (pa_tagstruct_getu32(t, &idx) < 0 ||
|
2007-10-28 19:13:50 +00:00
|
|
|
(command == PA_COMMAND_SET_SINK_MUTE && pa_tagstruct_gets(t, &name) < 0) ||
|
|
|
|
|
(command == PA_COMMAND_SET_SOURCE_MUTE && pa_tagstruct_gets(t, &name) < 0) ||
|
2006-02-23 12:04:31 +00:00
|
|
|
pa_tagstruct_get_boolean(t, &mute) ||
|
|
|
|
|
!pa_tagstruct_eof(t)) {
|
|
|
|
|
protocol_error(c);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-02-23 12:04:31 +00:00
|
|
|
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
|
2009-08-29 03:57:53 +02:00
|
|
|
CHECK_VALIDITY(c->pstream, !name || pa_namereg_is_valid_name_or_wildcard(name, command == PA_COMMAND_SET_SINK_MUTE ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE), tag, PA_ERR_INVALID);
|
2013-11-19 11:59:39 -09:00
|
|
|
CHECK_VALIDITY(c->pstream, (idx != PA_INVALID_INDEX) ^ (name != NULL), tag, PA_ERR_INVALID);
|
2006-02-23 12:04:31 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
switch (command) {
|
|
|
|
|
|
|
|
|
|
case PA_COMMAND_SET_SINK_MUTE:
|
|
|
|
|
if (idx != PA_INVALID_INDEX)
|
|
|
|
|
sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
|
|
|
|
|
else
|
2009-01-15 20:07:13 +01:00
|
|
|
sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK);
|
2007-10-28 19:13:50 +00:00
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PA_COMMAND_SET_SOURCE_MUTE:
|
|
|
|
|
if (idx != PA_INVALID_INDEX)
|
|
|
|
|
source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
|
|
|
|
|
else
|
2009-01-15 20:07:13 +01:00
|
|
|
source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE);
|
2007-10-28 19:13:50 +00:00
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PA_COMMAND_SET_SINK_INPUT_MUTE:
|
|
|
|
|
si = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx);
|
|
|
|
|
break;
|
|
|
|
|
|
2011-05-17 22:31:10 +01:00
|
|
|
case PA_COMMAND_SET_SOURCE_OUTPUT_MUTE:
|
|
|
|
|
so = pa_idxset_get_by_index(c->protocol->core->source_outputs, idx);
|
|
|
|
|
break;
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
default:
|
|
|
|
|
pa_assert_not_reached();
|
2006-02-23 12:04:31 +00:00
|
|
|
}
|
|
|
|
|
|
2011-05-17 22:31:10 +01:00
|
|
|
CHECK_VALIDITY(c->pstream, si || so || sink || source, tag, PA_ERR_NOENTITY);
|
2006-02-23 12:04:31 +00:00
|
|
|
|
2009-08-27 00:05:54 +02:00
|
|
|
client_name = pa_strnull(pa_proplist_gets(c->client->proplist, PA_PROP_APPLICATION_PROCESS_BINARY));
|
|
|
|
|
|
|
|
|
|
if (sink) {
|
|
|
|
|
pa_log_debug("Client %s changes mute of sink %s.", client_name, sink->name);
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_sink_set_mute(sink, mute, true);
|
2009-08-27 00:05:54 +02:00
|
|
|
} else if (source) {
|
|
|
|
|
pa_log_debug("Client %s changes mute of source %s.", client_name, source->name);
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_source_set_mute(source, mute, true);
|
2009-08-27 00:05:54 +02:00
|
|
|
} else if (si) {
|
|
|
|
|
pa_log_debug("Client %s changes mute of sink input %s.",
|
|
|
|
|
client_name,
|
|
|
|
|
pa_strnull(pa_proplist_gets(si->proplist, PA_PROP_MEDIA_NAME)));
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_sink_input_set_mute(si, mute, true);
|
2011-05-17 22:31:10 +01:00
|
|
|
} else if (so) {
|
|
|
|
|
pa_log_debug("Client %s changes mute of source output %s.",
|
|
|
|
|
client_name,
|
|
|
|
|
pa_strnull(pa_proplist_gets(so->proplist, PA_PROP_MEDIA_NAME)));
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_source_output_set_mute(so, mute, true);
|
2009-08-27 00:05:54 +02:00
|
|
|
}
|
2006-02-23 12:04:31 +00:00
|
|
|
|
|
|
|
|
pa_pstream_send_simple_ack(c->pstream, tag);
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-09 16:20:29 +02:00
|
|
|
static void command_cork_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
2006-01-11 01:17:39 +00:00
|
|
|
uint32_t idx;
|
2013-06-27 19:28:09 +02:00
|
|
|
bool b;
|
2007-10-28 19:13:50 +00:00
|
|
|
playback_stream *s;
|
|
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_assert_ref(c);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(t);
|
2004-08-22 21:13:58 +00:00
|
|
|
|
2006-01-11 01:17:39 +00:00
|
|
|
if (pa_tagstruct_getu32(t, &idx) < 0 ||
|
2004-09-26 17:02:26 +00:00
|
|
|
pa_tagstruct_get_boolean(t, &b) < 0 ||
|
2004-08-22 21:13:58 +00:00
|
|
|
!pa_tagstruct_eof(t)) {
|
|
|
|
|
protocol_error(c);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-22 18:54:21 +00:00
|
|
|
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
|
|
|
|
|
CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX, tag, PA_ERR_INVALID);
|
|
|
|
|
s = pa_idxset_get_by_index(c->output_streams, idx);
|
|
|
|
|
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
|
2007-10-28 19:13:50 +00:00
|
|
|
CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
|
2006-02-20 04:05:16 +00:00
|
|
|
|
2004-08-22 21:13:58 +00:00
|
|
|
pa_sink_input_cork(s->sink_input, b);
|
2008-10-06 02:26:08 +02:00
|
|
|
|
|
|
|
|
if (b)
|
2013-06-27 19:28:09 +02:00
|
|
|
s->is_underrun = true;
|
2008-10-06 02:26:08 +02:00
|
|
|
|
2006-02-20 04:05:16 +00:00
|
|
|
pa_pstream_send_simple_ack(c->pstream, tag);
|
2007-01-04 13:43:45 +00:00
|
|
|
}
|
2006-02-20 04:05:16 +00:00
|
|
|
|
2008-08-09 16:20:29 +02:00
|
|
|
static void command_trigger_or_flush_or_prebuf_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
2006-02-20 04:05:16 +00:00
|
|
|
uint32_t idx;
|
2007-10-28 19:13:50 +00:00
|
|
|
playback_stream *s;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_assert_ref(c);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(t);
|
2004-08-22 21:13:58 +00:00
|
|
|
|
2006-01-11 01:17:39 +00:00
|
|
|
if (pa_tagstruct_getu32(t, &idx) < 0 ||
|
2004-08-22 21:13:58 +00:00
|
|
|
!pa_tagstruct_eof(t)) {
|
|
|
|
|
protocol_error(c);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-22 18:54:21 +00:00
|
|
|
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
|
|
|
|
|
CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX, tag, PA_ERR_INVALID);
|
|
|
|
|
s = pa_idxset_get_by_index(c->output_streams, idx);
|
|
|
|
|
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
|
2007-10-28 19:13:50 +00:00
|
|
|
CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
|
2004-08-22 21:13:58 +00:00
|
|
|
|
2006-02-20 04:05:16 +00:00
|
|
|
switch (command) {
|
2007-10-28 19:13:50 +00:00
|
|
|
case PA_COMMAND_FLUSH_PLAYBACK_STREAM:
|
|
|
|
|
pa_asyncmsgq_send(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_FLUSH, NULL, 0, NULL);
|
|
|
|
|
break;
|
|
|
|
|
|
2006-02-20 04:05:16 +00:00
|
|
|
case PA_COMMAND_PREBUF_PLAYBACK_STREAM:
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_asyncmsgq_send(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_PREBUF_FORCE, NULL, 0, NULL);
|
2006-02-20 04:05:16 +00:00
|
|
|
break;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-02-20 04:05:16 +00:00
|
|
|
case PA_COMMAND_TRIGGER_PLAYBACK_STREAM:
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_asyncmsgq_send(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_TRIGGER, NULL, 0, NULL);
|
2006-02-20 04:05:16 +00:00
|
|
|
break;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-02-20 04:05:16 +00:00
|
|
|
default:
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert_not_reached();
|
2004-08-27 01:29:49 +00:00
|
|
|
}
|
|
|
|
|
|
2004-08-22 21:13:58 +00:00
|
|
|
pa_pstream_send_simple_ack(c->pstream, tag);
|
|
|
|
|
}
|
2004-08-15 13:15:51 +00:00
|
|
|
|
2008-08-09 16:20:29 +02:00
|
|
|
static void command_cork_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
2006-01-11 01:17:39 +00:00
|
|
|
uint32_t idx;
|
2007-10-28 19:13:50 +00:00
|
|
|
record_stream *s;
|
2013-06-27 19:28:09 +02:00
|
|
|
bool b;
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_assert_ref(c);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(t);
|
2004-09-26 17:02:26 +00:00
|
|
|
|
2006-01-11 01:17:39 +00:00
|
|
|
if (pa_tagstruct_getu32(t, &idx) < 0 ||
|
2004-09-26 17:02:26 +00:00
|
|
|
pa_tagstruct_get_boolean(t, &b) < 0 ||
|
|
|
|
|
!pa_tagstruct_eof(t)) {
|
|
|
|
|
protocol_error(c);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-22 18:54:21 +00:00
|
|
|
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
|
|
|
|
|
s = pa_idxset_get_by_index(c->record_streams, idx);
|
|
|
|
|
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
|
2004-09-26 17:02:26 +00:00
|
|
|
|
|
|
|
|
pa_source_output_cork(s->source_output, b);
|
2006-02-20 04:05:16 +00:00
|
|
|
pa_memblockq_prebuf_force(s->memblockq);
|
2004-09-26 17:02:26 +00:00
|
|
|
pa_pstream_send_simple_ack(c->pstream, tag);
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-09 16:20:29 +02:00
|
|
|
static void command_flush_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
2006-01-11 01:17:39 +00:00
|
|
|
uint32_t idx;
|
2007-10-28 19:13:50 +00:00
|
|
|
record_stream *s;
|
|
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_assert_ref(c);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(t);
|
2004-09-26 17:02:26 +00:00
|
|
|
|
2006-01-11 01:17:39 +00:00
|
|
|
if (pa_tagstruct_getu32(t, &idx) < 0 ||
|
2004-09-26 17:02:26 +00:00
|
|
|
!pa_tagstruct_eof(t)) {
|
|
|
|
|
protocol_error(c);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-22 18:54:21 +00:00
|
|
|
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
|
|
|
|
|
s = pa_idxset_get_by_index(c->record_streams, idx);
|
|
|
|
|
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
|
2004-09-26 17:02:26 +00:00
|
|
|
|
2008-06-26 00:39:31 +02:00
|
|
|
pa_memblockq_flush_read(s->memblockq);
|
2004-09-26 17:02:26 +00:00
|
|
|
pa_pstream_send_simple_ack(c->pstream, tag);
|
|
|
|
|
}
|
|
|
|
|
|
2007-11-21 01:30:40 +00:00
|
|
|
static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
2007-11-21 01:30:40 +00:00
|
|
|
uint32_t idx;
|
2009-03-30 18:46:12 +02:00
|
|
|
pa_buffer_attr a;
|
2007-11-21 01:30:40 +00:00
|
|
|
pa_tagstruct *reply;
|
|
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_assert_ref(c);
|
2007-11-21 01:30:40 +00:00
|
|
|
pa_assert(t);
|
|
|
|
|
|
2009-03-30 18:46:12 +02:00
|
|
|
memset(&a, 0, sizeof(a));
|
|
|
|
|
|
2007-11-21 01:30:40 +00:00
|
|
|
if (pa_tagstruct_getu32(t, &idx) < 0) {
|
|
|
|
|
protocol_error(c);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
|
|
|
|
|
|
|
|
|
|
if (command == PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR) {
|
|
|
|
|
playback_stream *s;
|
2013-06-27 19:28:09 +02:00
|
|
|
bool adjust_latency = false, early_requests = false;
|
2007-11-21 01:30:40 +00:00
|
|
|
|
|
|
|
|
s = pa_idxset_get_by_index(c->output_streams, idx);
|
|
|
|
|
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
|
|
|
|
|
CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
|
|
|
|
|
|
|
|
|
|
if (pa_tagstruct_get(
|
|
|
|
|
t,
|
2009-03-30 18:46:12 +02:00
|
|
|
PA_TAG_U32, &a.maxlength,
|
|
|
|
|
PA_TAG_U32, &a.tlength,
|
|
|
|
|
PA_TAG_U32, &a.prebuf,
|
|
|
|
|
PA_TAG_U32, &a.minreq,
|
2007-11-21 01:30:40 +00:00
|
|
|
PA_TAG_INVALID) < 0 ||
|
2008-05-15 23:34:41 +00:00
|
|
|
(c->version >= 13 && pa_tagstruct_get_boolean(t, &adjust_latency) < 0) ||
|
2008-09-03 18:31:46 +02:00
|
|
|
(c->version >= 14 && pa_tagstruct_get_boolean(t, &early_requests) < 0) ||
|
2007-11-21 01:30:40 +00:00
|
|
|
!pa_tagstruct_eof(t)) {
|
|
|
|
|
protocol_error(c);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-30 18:46:12 +02:00
|
|
|
s->adjust_latency = adjust_latency;
|
|
|
|
|
s->early_requests = early_requests;
|
2011-06-14 16:38:02 -07:00
|
|
|
s->buffer_attr_req = a;
|
2009-03-30 18:46:12 +02:00
|
|
|
|
|
|
|
|
fix_playback_buffer_attr(s);
|
|
|
|
|
pa_assert_se(pa_asyncmsgq_send(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_UPDATE_BUFFER_ATTR, NULL, 0, NULL) == 0);
|
2007-11-21 01:30:40 +00:00
|
|
|
|
|
|
|
|
reply = reply_new(tag);
|
2009-03-30 18:46:12 +02:00
|
|
|
pa_tagstruct_putu32(reply, s->buffer_attr.maxlength);
|
|
|
|
|
pa_tagstruct_putu32(reply, s->buffer_attr.tlength);
|
|
|
|
|
pa_tagstruct_putu32(reply, s->buffer_attr.prebuf);
|
|
|
|
|
pa_tagstruct_putu32(reply, s->buffer_attr.minreq);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
|
|
|
|
if (c->version >= 13)
|
2009-04-05 02:59:02 +02:00
|
|
|
pa_tagstruct_put_usec(reply, s->configured_sink_latency);
|
2007-11-21 01:30:40 +00:00
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
record_stream *s;
|
2013-06-27 19:28:09 +02:00
|
|
|
bool adjust_latency = false, early_requests = false;
|
2007-11-21 01:30:40 +00:00
|
|
|
pa_assert(command == PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR);
|
|
|
|
|
|
|
|
|
|
s = pa_idxset_get_by_index(c->record_streams, idx);
|
|
|
|
|
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
|
|
|
|
|
|
|
|
|
|
if (pa_tagstruct_get(
|
|
|
|
|
t,
|
2009-03-30 18:46:12 +02:00
|
|
|
PA_TAG_U32, &a.maxlength,
|
|
|
|
|
PA_TAG_U32, &a.fragsize,
|
2007-11-21 01:30:40 +00:00
|
|
|
PA_TAG_INVALID) < 0 ||
|
2008-05-15 23:34:41 +00:00
|
|
|
(c->version >= 13 && pa_tagstruct_get_boolean(t, &adjust_latency) < 0) ||
|
2008-09-03 18:31:46 +02:00
|
|
|
(c->version >= 14 && pa_tagstruct_get_boolean(t, &early_requests) < 0) ||
|
2007-11-21 01:30:40 +00:00
|
|
|
!pa_tagstruct_eof(t)) {
|
|
|
|
|
protocol_error(c);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-30 18:46:12 +02:00
|
|
|
s->adjust_latency = adjust_latency;
|
|
|
|
|
s->early_requests = early_requests;
|
2011-06-14 16:38:02 -07:00
|
|
|
s->buffer_attr_req = a;
|
2009-03-30 18:46:12 +02:00
|
|
|
|
|
|
|
|
fix_record_buffer_attr_pre(s);
|
|
|
|
|
pa_memblockq_set_maxlength(s->memblockq, s->buffer_attr.maxlength);
|
|
|
|
|
pa_memblockq_get_attr(s->memblockq, &s->buffer_attr);
|
|
|
|
|
fix_record_buffer_attr_post(s);
|
2007-11-21 01:30:40 +00:00
|
|
|
|
|
|
|
|
reply = reply_new(tag);
|
2009-03-30 18:46:12 +02:00
|
|
|
pa_tagstruct_putu32(reply, s->buffer_attr.maxlength);
|
|
|
|
|
pa_tagstruct_putu32(reply, s->buffer_attr.fragsize);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
|
|
|
|
if (c->version >= 13)
|
2009-04-05 02:59:02 +02:00
|
|
|
pa_tagstruct_put_usec(reply, s->configured_source_latency);
|
2007-11-21 01:30:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pa_pstream_send_tagstruct(c->pstream, reply);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void command_update_stream_sample_rate(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
2007-11-21 01:30:40 +00:00
|
|
|
uint32_t idx;
|
|
|
|
|
uint32_t rate;
|
|
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_assert_ref(c);
|
2007-11-21 01:30:40 +00:00
|
|
|
pa_assert(t);
|
|
|
|
|
|
|
|
|
|
if (pa_tagstruct_getu32(t, &idx) < 0 ||
|
|
|
|
|
pa_tagstruct_getu32(t, &rate) < 0 ||
|
|
|
|
|
!pa_tagstruct_eof(t)) {
|
|
|
|
|
protocol_error(c);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
|
2013-12-04 09:50:10 +02:00
|
|
|
CHECK_VALIDITY(c->pstream, pa_sample_rate_valid(rate), tag, PA_ERR_INVALID);
|
2007-11-21 01:30:40 +00:00
|
|
|
|
|
|
|
|
if (command == PA_COMMAND_UPDATE_PLAYBACK_STREAM_SAMPLE_RATE) {
|
|
|
|
|
playback_stream *s;
|
|
|
|
|
|
|
|
|
|
s = pa_idxset_get_by_index(c->output_streams, idx);
|
|
|
|
|
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
|
|
|
|
|
CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
|
|
|
|
|
|
|
|
|
|
pa_sink_input_set_rate(s->sink_input, rate);
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
record_stream *s;
|
|
|
|
|
pa_assert(command == PA_COMMAND_UPDATE_RECORD_STREAM_SAMPLE_RATE);
|
|
|
|
|
|
|
|
|
|
s = pa_idxset_get_by_index(c->record_streams, idx);
|
|
|
|
|
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
|
|
|
|
|
|
|
|
|
|
pa_source_output_set_rate(s->source_output, rate);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pa_pstream_send_simple_ack(c->pstream, tag);
|
|
|
|
|
}
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
static void command_update_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
2008-05-15 23:34:41 +00:00
|
|
|
uint32_t idx;
|
|
|
|
|
uint32_t mode;
|
|
|
|
|
pa_proplist *p;
|
|
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_assert_ref(c);
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_assert(t);
|
|
|
|
|
|
|
|
|
|
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
|
|
|
|
|
|
|
|
|
|
p = pa_proplist_new();
|
|
|
|
|
|
|
|
|
|
if (command == PA_COMMAND_UPDATE_CLIENT_PROPLIST) {
|
|
|
|
|
|
|
|
|
|
if (pa_tagstruct_getu32(t, &mode) < 0 ||
|
|
|
|
|
pa_tagstruct_get_proplist(t, p) < 0 ||
|
|
|
|
|
!pa_tagstruct_eof(t)) {
|
|
|
|
|
protocol_error(c);
|
|
|
|
|
pa_proplist_free(p);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
|
|
if (pa_tagstruct_getu32(t, &idx) < 0 ||
|
|
|
|
|
pa_tagstruct_getu32(t, &mode) < 0 ||
|
|
|
|
|
pa_tagstruct_get_proplist(t, p) < 0 ||
|
|
|
|
|
!pa_tagstruct_eof(t)) {
|
|
|
|
|
protocol_error(c);
|
|
|
|
|
pa_proplist_free(p);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-02-18 20:00:57 +02:00
|
|
|
if (!(mode == PA_UPDATE_SET || mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE)) {
|
|
|
|
|
pa_proplist_free(p);
|
2013-06-27 19:28:09 +02:00
|
|
|
CHECK_VALIDITY(c->pstream, false, tag, PA_ERR_INVALID);
|
2009-02-18 20:00:57 +02:00
|
|
|
}
|
2008-05-15 23:34:41 +00:00
|
|
|
|
|
|
|
|
if (command == PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST) {
|
|
|
|
|
playback_stream *s;
|
|
|
|
|
|
|
|
|
|
s = pa_idxset_get_by_index(c->output_streams, idx);
|
2009-02-18 20:00:57 +02:00
|
|
|
if (!s || !playback_stream_isinstance(s)) {
|
|
|
|
|
pa_proplist_free(p);
|
2013-06-27 19:28:09 +02:00
|
|
|
CHECK_VALIDITY(c->pstream, false, tag, PA_ERR_NOENTITY);
|
2009-02-18 20:00:57 +02:00
|
|
|
}
|
2009-02-05 04:10:08 +01:00
|
|
|
pa_sink_input_update_proplist(s->sink_input, mode, p);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
|
|
|
|
} else if (command == PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST) {
|
|
|
|
|
record_stream *s;
|
|
|
|
|
|
2009-02-18 20:00:57 +02:00
|
|
|
if (!(s = pa_idxset_get_by_index(c->record_streams, idx))) {
|
|
|
|
|
pa_proplist_free(p);
|
2013-06-27 19:28:09 +02:00
|
|
|
CHECK_VALIDITY(c->pstream, false, tag, PA_ERR_NOENTITY);
|
2009-02-18 20:00:57 +02:00
|
|
|
}
|
2009-02-05 04:10:08 +01:00
|
|
|
pa_source_output_update_proplist(s->source_output, mode, p);
|
2009-02-18 20:00:57 +02:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
} else {
|
|
|
|
|
pa_assert(command == PA_COMMAND_UPDATE_CLIENT_PROPLIST);
|
|
|
|
|
|
2009-02-05 04:10:08 +01:00
|
|
|
pa_client_update_proplist(c->client, mode, p);
|
2008-05-15 23:34:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pa_pstream_send_simple_ack(c->pstream, tag);
|
2009-02-18 20:00:57 +02:00
|
|
|
pa_proplist_free(p);
|
2008-05-15 23:34:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void command_remove_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
2008-05-15 23:34:41 +00:00
|
|
|
uint32_t idx;
|
|
|
|
|
unsigned changed = 0;
|
|
|
|
|
pa_proplist *p;
|
|
|
|
|
pa_strlist *l = NULL;
|
|
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_assert_ref(c);
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_assert(t);
|
|
|
|
|
|
|
|
|
|
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
|
|
|
|
|
|
|
|
|
|
if (command != PA_COMMAND_REMOVE_CLIENT_PROPLIST) {
|
|
|
|
|
|
|
|
|
|
if (pa_tagstruct_getu32(t, &idx) < 0) {
|
|
|
|
|
protocol_error(c);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (command == PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST) {
|
|
|
|
|
playback_stream *s;
|
|
|
|
|
|
|
|
|
|
s = pa_idxset_get_by_index(c->output_streams, idx);
|
|
|
|
|
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
|
|
|
|
|
CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
|
|
|
|
|
|
|
|
|
|
p = s->sink_input->proplist;
|
|
|
|
|
|
|
|
|
|
} else if (command == PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST) {
|
|
|
|
|
record_stream *s;
|
|
|
|
|
|
|
|
|
|
s = pa_idxset_get_by_index(c->record_streams, idx);
|
|
|
|
|
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
|
|
|
|
|
|
|
|
|
|
p = s->source_output->proplist;
|
|
|
|
|
} else {
|
|
|
|
|
pa_assert(command == PA_COMMAND_REMOVE_CLIENT_PROPLIST);
|
|
|
|
|
|
|
|
|
|
p = c->client->proplist;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
|
const char *k;
|
|
|
|
|
|
|
|
|
|
if (pa_tagstruct_gets(t, &k) < 0) {
|
|
|
|
|
protocol_error(c);
|
|
|
|
|
pa_strlist_free(l);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!k)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
l = pa_strlist_prepend(l, k);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!pa_tagstruct_eof(t)) {
|
|
|
|
|
protocol_error(c);
|
|
|
|
|
pa_strlist_free(l);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
|
char *z;
|
|
|
|
|
|
|
|
|
|
l = pa_strlist_pop(l, &z);
|
|
|
|
|
|
|
|
|
|
if (!z)
|
|
|
|
|
break;
|
|
|
|
|
|
2008-08-19 22:39:54 +02:00
|
|
|
changed += (unsigned) (pa_proplist_unset(p, z) >= 0);
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_xfree(z);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pa_pstream_send_simple_ack(c->pstream, tag);
|
|
|
|
|
|
|
|
|
|
if (changed) {
|
|
|
|
|
if (command == PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST) {
|
|
|
|
|
playback_stream *s;
|
|
|
|
|
|
|
|
|
|
s = pa_idxset_get_by_index(c->output_streams, idx);
|
|
|
|
|
pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, s->sink_input->index);
|
|
|
|
|
|
|
|
|
|
} else if (command == PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST) {
|
|
|
|
|
record_stream *s;
|
|
|
|
|
|
|
|
|
|
s = pa_idxset_get_by_index(c->record_streams, idx);
|
|
|
|
|
pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, s->source_output->index);
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
pa_assert(command == PA_COMMAND_REMOVE_CLIENT_PROPLIST);
|
|
|
|
|
pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->client->index);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-09 16:20:29 +02:00
|
|
|
static void command_set_default_sink_or_source(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
2004-09-06 21:55:09 +00:00
|
|
|
const char *s;
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_assert_ref(c);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(t);
|
2004-09-06 21:55:09 +00:00
|
|
|
|
2006-02-22 18:54:21 +00:00
|
|
|
if (pa_tagstruct_gets(t, &s) < 0 ||
|
2004-09-06 21:55:09 +00:00
|
|
|
!pa_tagstruct_eof(t)) {
|
|
|
|
|
protocol_error(c);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-22 18:54:21 +00:00
|
|
|
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
|
2008-08-18 17:48:04 +02:00
|
|
|
CHECK_VALIDITY(c->pstream, !s || pa_namereg_is_valid_name(s), tag, PA_ERR_INVALID);
|
2004-09-06 21:55:09 +00:00
|
|
|
|
2009-01-28 01:46:27 +01:00
|
|
|
if (command == PA_COMMAND_SET_DEFAULT_SOURCE) {
|
|
|
|
|
pa_source *source;
|
|
|
|
|
|
|
|
|
|
source = pa_namereg_get(c->protocol->core, s, PA_NAMEREG_SOURCE);
|
|
|
|
|
CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY);
|
|
|
|
|
|
2017-06-30 00:09:34 +03:00
|
|
|
pa_core_set_configured_default_source(c->protocol->core, source->name);
|
2009-01-28 01:46:27 +01:00
|
|
|
} else {
|
|
|
|
|
pa_sink *sink;
|
|
|
|
|
pa_assert(command == PA_COMMAND_SET_DEFAULT_SINK);
|
|
|
|
|
|
|
|
|
|
sink = pa_namereg_get(c->protocol->core, s, PA_NAMEREG_SINK);
|
|
|
|
|
CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
|
|
|
|
|
|
2017-06-30 00:09:34 +03:00
|
|
|
pa_core_set_configured_default_sink(c->protocol->core, sink->name);
|
2009-01-28 01:46:27 +01:00
|
|
|
}
|
|
|
|
|
|
2004-09-06 21:55:09 +00:00
|
|
|
pa_pstream_send_simple_ack(c->pstream, tag);
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-09 16:20:29 +02:00
|
|
|
static void command_set_stream_name(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
2006-01-11 01:17:39 +00:00
|
|
|
uint32_t idx;
|
2004-09-15 19:16:57 +00:00
|
|
|
const char *name;
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_assert_ref(c);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(t);
|
2004-09-15 19:16:57 +00:00
|
|
|
|
2006-01-11 01:17:39 +00:00
|
|
|
if (pa_tagstruct_getu32(t, &idx) < 0 ||
|
2006-02-22 18:54:21 +00:00
|
|
|
pa_tagstruct_gets(t, &name) < 0 ||
|
2004-09-15 19:16:57 +00:00
|
|
|
!pa_tagstruct_eof(t)) {
|
|
|
|
|
protocol_error(c);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-02-22 18:54:21 +00:00
|
|
|
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
|
2006-05-14 16:02:40 +00:00
|
|
|
CHECK_VALIDITY(c->pstream, name && pa_utf8_valid(name), tag, PA_ERR_INVALID);
|
2004-09-15 19:16:57 +00:00
|
|
|
|
|
|
|
|
if (command == PA_COMMAND_SET_PLAYBACK_STREAM_NAME) {
|
2007-10-28 19:13:50 +00:00
|
|
|
playback_stream *s;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-02-22 18:54:21 +00:00
|
|
|
s = pa_idxset_get_by_index(c->output_streams, idx);
|
|
|
|
|
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
|
2007-10-28 19:13:50 +00:00
|
|
|
CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
|
2004-09-15 19:16:57 +00:00
|
|
|
|
2016-03-13 13:57:31 +02:00
|
|
|
pa_sink_input_set_property(s->sink_input, PA_PROP_MEDIA_NAME, name);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-09-15 19:16:57 +00:00
|
|
|
} else {
|
2007-10-28 19:13:50 +00:00
|
|
|
record_stream *s;
|
2007-11-21 01:30:40 +00:00
|
|
|
pa_assert(command == PA_COMMAND_SET_RECORD_STREAM_NAME);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-02-22 18:54:21 +00:00
|
|
|
s = pa_idxset_get_by_index(c->record_streams, idx);
|
|
|
|
|
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
|
2004-09-15 19:16:57 +00:00
|
|
|
|
2016-03-13 13:57:31 +02:00
|
|
|
pa_source_output_set_property(s->source_output, PA_PROP_MEDIA_NAME, name);
|
2004-09-15 19:16:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pa_pstream_send_simple_ack(c->pstream, tag);
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-09 16:20:29 +02:00
|
|
|
static void command_kill(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
2006-01-11 01:17:39 +00:00
|
|
|
uint32_t idx;
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_assert_ref(c);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(t);
|
2004-09-15 19:16:57 +00:00
|
|
|
|
2006-01-11 01:17:39 +00:00
|
|
|
if (pa_tagstruct_getu32(t, &idx) < 0 ||
|
2004-09-15 19:16:57 +00:00
|
|
|
!pa_tagstruct_eof(t)) {
|
|
|
|
|
protocol_error(c);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2006-02-22 18:54:21 +00:00
|
|
|
|
|
|
|
|
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
|
2004-09-15 19:16:57 +00:00
|
|
|
|
|
|
|
|
if (command == PA_COMMAND_KILL_CLIENT) {
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_client *client;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-02-22 18:54:21 +00:00
|
|
|
client = pa_idxset_get_by_index(c->protocol->core->clients, idx);
|
|
|
|
|
CHECK_VALIDITY(c->pstream, client, tag, PA_ERR_NOENTITY);
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_ref(c);
|
2004-09-15 19:16:57 +00:00
|
|
|
pa_client_kill(client);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-09-15 19:16:57 +00:00
|
|
|
} else if (command == PA_COMMAND_KILL_SINK_INPUT) {
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_sink_input *s;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-02-22 18:54:21 +00:00
|
|
|
s = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx);
|
|
|
|
|
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
|
2004-09-15 19:16:57 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_ref(c);
|
2004-09-15 19:16:57 +00:00
|
|
|
pa_sink_input_kill(s);
|
|
|
|
|
} else {
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_source_output *s;
|
2004-09-15 19:16:57 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(command == PA_COMMAND_KILL_SOURCE_OUTPUT);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-02-22 18:54:21 +00:00
|
|
|
s = pa_idxset_get_by_index(c->protocol->core->source_outputs, idx);
|
|
|
|
|
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
|
2004-09-15 19:16:57 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_ref(c);
|
2004-09-15 19:16:57 +00:00
|
|
|
pa_source_output_kill(s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pa_pstream_send_simple_ack(c->pstream, tag);
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_unref(c);
|
2004-09-15 19:16:57 +00:00
|
|
|
}
|
|
|
|
|
|
2008-08-09 16:20:29 +02:00
|
|
|
static void command_load_module(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_module *m;
|
2004-09-15 19:16:57 +00:00
|
|
|
const char *name, *argument;
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_tagstruct *reply;
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_assert_ref(c);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(t);
|
2004-09-15 19:16:57 +00:00
|
|
|
|
2006-02-22 18:54:21 +00:00
|
|
|
if (pa_tagstruct_gets(t, &name) < 0 ||
|
2004-09-15 19:16:57 +00:00
|
|
|
pa_tagstruct_gets(t, &argument) < 0 ||
|
|
|
|
|
!pa_tagstruct_eof(t)) {
|
|
|
|
|
protocol_error(c);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2006-02-22 18:54:21 +00:00
|
|
|
|
|
|
|
|
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
|
2006-05-14 16:02:40 +00:00
|
|
|
CHECK_VALIDITY(c->pstream, name && *name && pa_utf8_valid(name) && !strchr(name, '/'), tag, PA_ERR_INVALID);
|
|
|
|
|
CHECK_VALIDITY(c->pstream, !argument || pa_utf8_valid(argument), tag, PA_ERR_INVALID);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2017-08-26 11:21:15 +02:00
|
|
|
if (pa_module_load(&m, c->protocol->core, name, argument) < 0) {
|
2006-02-20 04:05:16 +00:00
|
|
|
pa_pstream_send_error(c->pstream, tag, PA_ERR_MODINITFAILED);
|
2004-09-15 19:16:57 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-22 18:54:21 +00:00
|
|
|
reply = reply_new(tag);
|
2004-09-15 19:16:57 +00:00
|
|
|
pa_tagstruct_putu32(reply, m->index);
|
|
|
|
|
pa_pstream_send_tagstruct(c->pstream, reply);
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-09 16:20:29 +02:00
|
|
|
static void command_unload_module(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
2006-01-11 01:17:39 +00:00
|
|
|
uint32_t idx;
|
|
|
|
|
pa_module *m;
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_assert_ref(c);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(t);
|
2004-09-15 19:16:57 +00:00
|
|
|
|
2006-01-11 01:17:39 +00:00
|
|
|
if (pa_tagstruct_getu32(t, &idx) < 0 ||
|
2004-09-15 19:16:57 +00:00
|
|
|
!pa_tagstruct_eof(t)) {
|
|
|
|
|
protocol_error(c);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-02-22 18:54:21 +00:00
|
|
|
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
|
|
|
|
|
m = pa_idxset_get_by_index(c->protocol->core->modules, idx);
|
|
|
|
|
CHECK_VALIDITY(c->pstream, m, tag, PA_ERR_NOENTITY);
|
2004-09-15 19:16:57 +00:00
|
|
|
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_module_unload_request(m, false);
|
2004-09-15 19:16:57 +00:00
|
|
|
pa_pstream_send_simple_ack(c->pstream, tag);
|
|
|
|
|
}
|
|
|
|
|
|
2006-07-31 21:54:46 +00:00
|
|
|
static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
2006-07-31 21:54:46 +00:00
|
|
|
uint32_t idx = PA_INVALID_INDEX, idx_device = PA_INVALID_INDEX;
|
2008-08-18 17:48:04 +02:00
|
|
|
const char *name_device = NULL;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_assert_ref(c);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(t);
|
2006-07-31 21:54:46 +00:00
|
|
|
|
|
|
|
|
if (pa_tagstruct_getu32(t, &idx) < 0 ||
|
|
|
|
|
pa_tagstruct_getu32(t, &idx_device) < 0 ||
|
2008-08-18 17:48:04 +02:00
|
|
|
pa_tagstruct_gets(t, &name_device) < 0 ||
|
2006-07-31 21:54:46 +00:00
|
|
|
!pa_tagstruct_eof(t)) {
|
|
|
|
|
protocol_error(c);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-07-31 21:54:46 +00:00
|
|
|
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
|
|
|
|
|
CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX, tag, PA_ERR_INVALID);
|
2008-08-18 17:48:04 +02:00
|
|
|
|
2009-08-29 03:57:53 +02:00
|
|
|
CHECK_VALIDITY(c->pstream, !name_device || pa_namereg_is_valid_name_or_wildcard(name_device, command == PA_COMMAND_MOVE_SINK_INPUT ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE), tag, PA_ERR_INVALID);
|
2013-11-19 11:59:39 -09:00
|
|
|
CHECK_VALIDITY(c->pstream, (idx_device != PA_INVALID_INDEX) ^ (name_device != NULL), tag, PA_ERR_INVALID);
|
2006-07-31 21:54:46 +00:00
|
|
|
|
2006-08-03 22:32:23 +00:00
|
|
|
if (command == PA_COMMAND_MOVE_SINK_INPUT) {
|
|
|
|
|
pa_sink_input *si = NULL;
|
|
|
|
|
pa_sink *sink = NULL;
|
2006-07-31 21:54:46 +00:00
|
|
|
|
2006-08-03 22:32:23 +00:00
|
|
|
si = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-08-03 22:32:23 +00:00
|
|
|
if (idx_device != PA_INVALID_INDEX)
|
|
|
|
|
sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx_device);
|
|
|
|
|
else
|
2009-01-15 20:07:13 +01:00
|
|
|
sink = pa_namereg_get(c->protocol->core, name_device, PA_NAMEREG_SINK);
|
2006-08-03 22:32:23 +00:00
|
|
|
|
|
|
|
|
CHECK_VALIDITY(c->pstream, si && sink, tag, PA_ERR_NOENTITY);
|
|
|
|
|
|
2013-06-27 19:28:09 +02:00
|
|
|
if (pa_sink_input_move_to(si, sink, true) < 0) {
|
2006-08-03 22:32:23 +00:00
|
|
|
pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
pa_source_output *so = NULL;
|
|
|
|
|
pa_source *source;
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(command == PA_COMMAND_MOVE_SOURCE_OUTPUT);
|
|
|
|
|
|
2006-08-03 22:32:23 +00:00
|
|
|
so = pa_idxset_get_by_index(c->protocol->core->source_outputs, idx);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-08-03 22:32:23 +00:00
|
|
|
if (idx_device != PA_INVALID_INDEX)
|
|
|
|
|
source = pa_idxset_get_by_index(c->protocol->core->sources, idx_device);
|
|
|
|
|
else
|
2009-01-15 20:07:13 +01:00
|
|
|
source = pa_namereg_get(c->protocol->core, name_device, PA_NAMEREG_SOURCE);
|
2006-08-03 22:32:23 +00:00
|
|
|
|
|
|
|
|
CHECK_VALIDITY(c->pstream, so && source, tag, PA_ERR_NOENTITY);
|
2006-07-31 21:54:46 +00:00
|
|
|
|
2013-06-27 19:28:09 +02:00
|
|
|
if (pa_source_output_move_to(so, source, true) < 0) {
|
2006-08-03 22:32:23 +00:00
|
|
|
pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-07-31 21:54:46 +00:00
|
|
|
pa_pstream_send_simple_ack(c->pstream, tag);
|
2007-10-28 19:13:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
2007-10-28 19:13:50 +00:00
|
|
|
uint32_t idx = PA_INVALID_INDEX;
|
|
|
|
|
const char *name = NULL;
|
2013-06-27 19:28:09 +02:00
|
|
|
bool b;
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_assert_ref(c);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(t);
|
|
|
|
|
|
|
|
|
|
if (pa_tagstruct_getu32(t, &idx) < 0 ||
|
|
|
|
|
pa_tagstruct_gets(t, &name) < 0 ||
|
|
|
|
|
pa_tagstruct_get_boolean(t, &b) < 0 ||
|
|
|
|
|
!pa_tagstruct_eof(t)) {
|
|
|
|
|
protocol_error(c);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
|
2009-08-29 03:57:53 +02:00
|
|
|
CHECK_VALIDITY(c->pstream, !name || pa_namereg_is_valid_name_or_wildcard(name, command == PA_COMMAND_SUSPEND_SINK ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE) || *name == 0, tag, PA_ERR_INVALID);
|
2013-11-19 11:59:39 -09:00
|
|
|
CHECK_VALIDITY(c->pstream, (idx != PA_INVALID_INDEX) ^ (name != NULL), tag, PA_ERR_INVALID);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
if (command == PA_COMMAND_SUSPEND_SINK) {
|
|
|
|
|
|
|
|
|
|
if (idx == PA_INVALID_INDEX && name && !*name) {
|
|
|
|
|
|
2009-01-12 19:44:21 +01:00
|
|
|
pa_log_debug("%s all sinks", b ? "Suspending" : "Resuming");
|
|
|
|
|
|
2009-06-05 19:05:07 +02:00
|
|
|
if (pa_sink_suspend_all(c->protocol->core, b, PA_SUSPEND_USER) < 0) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
pa_sink *sink = NULL;
|
|
|
|
|
|
|
|
|
|
if (idx != PA_INVALID_INDEX)
|
|
|
|
|
sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
|
|
|
|
|
else
|
2009-01-15 20:07:13 +01:00
|
|
|
sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK);
|
2007-10-28 19:13:50 +00:00
|
|
|
|
|
|
|
|
CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
|
|
|
|
|
|
2012-11-16 18:24:34 +02:00
|
|
|
pa_log_debug("%s of sink %s requested by client %" PRIu32 ".",
|
|
|
|
|
b ? "Suspending" : "Resuming", sink->name, c->client->index);
|
|
|
|
|
|
2009-06-05 19:05:07 +02:00
|
|
|
if (pa_sink_suspend(sink, b, PA_SUSPEND_USER) < 0) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
|
|
pa_assert(command == PA_COMMAND_SUSPEND_SOURCE);
|
|
|
|
|
|
|
|
|
|
if (idx == PA_INVALID_INDEX && name && !*name) {
|
|
|
|
|
|
2009-01-12 19:44:21 +01:00
|
|
|
pa_log_debug("%s all sources", b ? "Suspending" : "Resuming");
|
|
|
|
|
|
2009-06-05 19:05:07 +02:00
|
|
|
if (pa_source_suspend_all(c->protocol->core, b, PA_SUSPEND_USER) < 0) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
pa_source *source;
|
|
|
|
|
|
|
|
|
|
if (idx != PA_INVALID_INDEX)
|
|
|
|
|
source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
|
|
|
|
|
else
|
2009-01-15 20:07:13 +01:00
|
|
|
source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE);
|
2007-10-28 19:13:50 +00:00
|
|
|
|
|
|
|
|
CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY);
|
|
|
|
|
|
2012-11-16 18:24:34 +02:00
|
|
|
pa_log_debug("%s of source %s requested by client %" PRIu32 ".",
|
|
|
|
|
b ? "Suspending" : "Resuming", source->name, c->client->index);
|
|
|
|
|
|
2009-06-05 19:05:07 +02:00
|
|
|
if (pa_source_suspend(source, b, PA_SUSPEND_USER) < 0) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pa_pstream_send_simple_ack(c->pstream, tag);
|
2006-07-31 21:54:46 +00:00
|
|
|
}
|
|
|
|
|
|
2008-08-03 18:56:10 +02:00
|
|
|
static void command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
2008-08-03 18:56:10 +02:00
|
|
|
uint32_t idx = PA_INVALID_INDEX;
|
|
|
|
|
const char *name = NULL;
|
|
|
|
|
pa_module *m;
|
|
|
|
|
pa_native_protocol_ext_cb_t cb;
|
|
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_assert_ref(c);
|
2008-08-03 18:56:10 +02:00
|
|
|
pa_assert(t);
|
|
|
|
|
|
|
|
|
|
if (pa_tagstruct_getu32(t, &idx) < 0 ||
|
|
|
|
|
pa_tagstruct_gets(t, &name) < 0) {
|
|
|
|
|
protocol_error(c);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
|
2008-08-18 17:48:04 +02:00
|
|
|
CHECK_VALIDITY(c->pstream, !name || pa_utf8_valid(name), tag, PA_ERR_INVALID);
|
2013-11-19 11:59:39 -09:00
|
|
|
CHECK_VALIDITY(c->pstream, (idx != PA_INVALID_INDEX) ^ (name != NULL), tag, PA_ERR_INVALID);
|
2008-08-03 18:56:10 +02:00
|
|
|
|
|
|
|
|
if (idx != PA_INVALID_INDEX)
|
|
|
|
|
m = pa_idxset_get_by_index(c->protocol->core->modules, idx);
|
2012-05-21 21:42:20 +02:00
|
|
|
else
|
|
|
|
|
PA_IDXSET_FOREACH(m, c->protocol->core->modules, idx)
|
|
|
|
|
if (pa_streq(name, m->name))
|
2008-08-03 18:56:10 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
CHECK_VALIDITY(c->pstream, m, tag, PA_ERR_NOEXTENSION);
|
|
|
|
|
CHECK_VALIDITY(c->pstream, m->load_once || idx != PA_INVALID_INDEX, tag, PA_ERR_INVALID);
|
|
|
|
|
|
2009-01-22 00:22:46 +01:00
|
|
|
cb = (pa_native_protocol_ext_cb_t) (unsigned long) pa_hashmap_get(c->protocol->extensions, m);
|
2009-02-17 22:32:40 +02:00
|
|
|
CHECK_VALIDITY(c->pstream, cb, tag, PA_ERR_NOEXTENSION);
|
2008-08-03 18:56:10 +02:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
if (cb(c->protocol, m, c, tag, t) < 0)
|
|
|
|
|
protocol_error(c);
|
2008-08-03 18:56:10 +02:00
|
|
|
}
|
|
|
|
|
|
2020-01-14 10:15:36 +01:00
|
|
|
/* Send message to an object which registered a handler. Result must be returned as string. */
|
|
|
|
|
static void command_send_object_message(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
|
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
|
|
|
|
const char *object_path = NULL;
|
|
|
|
|
const char *message = NULL;
|
|
|
|
|
const char *message_parameters = NULL;
|
|
|
|
|
const char *client_name;
|
|
|
|
|
char *response = NULL;
|
|
|
|
|
int ret;
|
|
|
|
|
pa_tagstruct *reply;
|
|
|
|
|
|
|
|
|
|
pa_native_connection_assert_ref(c);
|
|
|
|
|
pa_assert(t);
|
|
|
|
|
|
|
|
|
|
if (pa_tagstruct_gets(t, &object_path) < 0 ||
|
|
|
|
|
pa_tagstruct_gets(t, &message) < 0 ||
|
|
|
|
|
pa_tagstruct_gets(t, &message_parameters) < 0 ||
|
|
|
|
|
!pa_tagstruct_eof(t)) {
|
|
|
|
|
protocol_error(c);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
|
|
|
|
|
CHECK_VALIDITY(c->pstream, object_path != NULL, tag, PA_ERR_INVALID);
|
|
|
|
|
CHECK_VALIDITY(c->pstream, pa_utf8_valid(object_path), tag, PA_ERR_INVALID);
|
|
|
|
|
CHECK_VALIDITY(c->pstream, message != NULL, tag, PA_ERR_INVALID);
|
|
|
|
|
CHECK_VALIDITY(c->pstream, pa_utf8_valid(message), tag, PA_ERR_INVALID);
|
|
|
|
|
if (message_parameters)
|
|
|
|
|
CHECK_VALIDITY(c->pstream, pa_utf8_valid(message_parameters), tag, PA_ERR_INVALID);
|
|
|
|
|
|
|
|
|
|
client_name = pa_strnull(pa_proplist_gets(c->client->proplist, PA_PROP_APPLICATION_PROCESS_BINARY));
|
|
|
|
|
pa_log_debug("Client %s sent message %s to path %s", client_name, message, object_path);
|
|
|
|
|
if (message_parameters)
|
|
|
|
|
pa_log_debug("Message parameters: %s", message_parameters);
|
|
|
|
|
|
|
|
|
|
ret = pa_message_handler_send_message(c->protocol->core, object_path, message, message_parameters, &response);
|
|
|
|
|
|
|
|
|
|
if (ret < 0) {
|
|
|
|
|
pa_pstream_send_error(c->pstream, tag, -ret);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
reply = reply_new(tag);
|
|
|
|
|
pa_tagstruct_puts(reply, response);
|
|
|
|
|
pa_xfree(response);
|
|
|
|
|
|
|
|
|
|
pa_pstream_send_tagstruct(c->pstream, reply);
|
|
|
|
|
}
|
|
|
|
|
|
2009-01-21 03:04:04 +01:00
|
|
|
static void command_set_card_profile(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
|
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
|
|
|
|
uint32_t idx = PA_INVALID_INDEX;
|
2013-11-20 15:42:26 +02:00
|
|
|
const char *name = NULL, *profile_name = NULL;
|
2009-01-21 03:04:04 +01:00
|
|
|
pa_card *card = NULL;
|
2013-11-20 15:42:26 +02:00
|
|
|
pa_card_profile *profile;
|
2009-06-17 04:52:41 +02:00
|
|
|
int ret;
|
2009-01-21 03:04:04 +01:00
|
|
|
|
|
|
|
|
pa_native_connection_assert_ref(c);
|
|
|
|
|
pa_assert(t);
|
|
|
|
|
|
|
|
|
|
if (pa_tagstruct_getu32(t, &idx) < 0 ||
|
|
|
|
|
pa_tagstruct_gets(t, &name) < 0 ||
|
2013-11-20 15:42:26 +02:00
|
|
|
pa_tagstruct_gets(t, &profile_name) < 0 ||
|
2009-01-21 03:04:04 +01:00
|
|
|
!pa_tagstruct_eof(t)) {
|
|
|
|
|
protocol_error(c);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
|
|
|
|
|
CHECK_VALIDITY(c->pstream, !name || pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID);
|
2013-11-19 11:59:39 -09:00
|
|
|
CHECK_VALIDITY(c->pstream, (idx != PA_INVALID_INDEX) ^ (name != NULL), tag, PA_ERR_INVALID);
|
2013-11-20 15:42:26 +02:00
|
|
|
CHECK_VALIDITY(c->pstream, profile_name, tag, PA_ERR_INVALID);
|
2009-01-21 03:04:04 +01:00
|
|
|
|
|
|
|
|
if (idx != PA_INVALID_INDEX)
|
|
|
|
|
card = pa_idxset_get_by_index(c->protocol->core->cards, idx);
|
|
|
|
|
else
|
|
|
|
|
card = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_CARD);
|
|
|
|
|
|
|
|
|
|
CHECK_VALIDITY(c->pstream, card, tag, PA_ERR_NOENTITY);
|
|
|
|
|
|
2013-11-20 15:42:26 +02:00
|
|
|
profile = pa_hashmap_get(card->profiles, profile_name);
|
|
|
|
|
|
|
|
|
|
CHECK_VALIDITY(c->pstream, profile, tag, PA_ERR_NOENTITY);
|
|
|
|
|
|
2017-04-04 12:46:25 +03:00
|
|
|
pa_log_info("Application \"%s\" requests card profile change. card = %s, profile = %s",
|
|
|
|
|
pa_strnull(pa_proplist_gets(c->client->proplist, PA_PROP_APPLICATION_NAME)),
|
|
|
|
|
card->name,
|
|
|
|
|
profile->name);
|
|
|
|
|
|
2013-06-27 19:28:09 +02:00
|
|
|
if ((ret = pa_card_set_profile(card, profile, true)) < 0) {
|
2009-06-17 04:52:41 +02:00
|
|
|
pa_pstream_send_error(c->pstream, tag, -ret);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pa_pstream_send_simple_ack(c->pstream, tag);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void command_set_sink_or_source_port(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
|
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
|
|
|
|
uint32_t idx = PA_INVALID_INDEX;
|
|
|
|
|
const char *name = NULL, *port = NULL;
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
pa_native_connection_assert_ref(c);
|
|
|
|
|
pa_assert(t);
|
|
|
|
|
|
|
|
|
|
if (pa_tagstruct_getu32(t, &idx) < 0 ||
|
|
|
|
|
pa_tagstruct_gets(t, &name) < 0 ||
|
|
|
|
|
pa_tagstruct_gets(t, &port) < 0 ||
|
|
|
|
|
!pa_tagstruct_eof(t)) {
|
|
|
|
|
protocol_error(c);
|
2009-01-21 03:04:04 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-17 04:52:41 +02:00
|
|
|
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
|
2009-08-29 03:57:53 +02:00
|
|
|
CHECK_VALIDITY(c->pstream, !name || pa_namereg_is_valid_name_or_wildcard(name, command == PA_COMMAND_SET_SINK_PORT ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE), tag, PA_ERR_INVALID);
|
2012-03-19 14:16:04 +05:30
|
|
|
CHECK_VALIDITY(c->pstream, (idx != PA_INVALID_INDEX) ^ (name != NULL), tag, PA_ERR_INVALID);
|
2012-03-16 14:38:38 +01:00
|
|
|
CHECK_VALIDITY(c->pstream, port, tag, PA_ERR_INVALID);
|
2009-06-17 04:52:41 +02:00
|
|
|
|
|
|
|
|
if (command == PA_COMMAND_SET_SINK_PORT) {
|
|
|
|
|
pa_sink *sink;
|
|
|
|
|
|
|
|
|
|
if (idx != PA_INVALID_INDEX)
|
|
|
|
|
sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
|
|
|
|
|
else
|
|
|
|
|
sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK);
|
|
|
|
|
|
|
|
|
|
CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
|
|
|
|
|
|
2013-06-27 19:28:09 +02:00
|
|
|
if ((ret = pa_sink_set_port(sink, port, true)) < 0) {
|
2009-06-17 04:52:41 +02:00
|
|
|
pa_pstream_send_error(c->pstream, tag, -ret);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
pa_source *source;
|
|
|
|
|
|
2013-12-16 18:01:37 +01:00
|
|
|
pa_assert(command == PA_COMMAND_SET_SOURCE_PORT);
|
2009-06-17 04:52:41 +02:00
|
|
|
|
|
|
|
|
if (idx != PA_INVALID_INDEX)
|
|
|
|
|
source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
|
|
|
|
|
else
|
|
|
|
|
source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE);
|
|
|
|
|
|
|
|
|
|
CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY);
|
|
|
|
|
|
2013-06-27 19:28:09 +02:00
|
|
|
if ((ret = pa_source_set_port(source, port, true)) < 0) {
|
2009-06-17 04:52:41 +02:00
|
|
|
pa_pstream_send_error(c->pstream, tag, -ret);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-01-21 03:04:04 +01:00
|
|
|
pa_pstream_send_simple_ack(c->pstream, tag);
|
|
|
|
|
}
|
2008-08-03 18:56:10 +02:00
|
|
|
|
2012-06-28 15:00:45 +02:00
|
|
|
static void command_set_port_latency_offset(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
|
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
|
|
|
|
const char *port_name, *card_name;
|
|
|
|
|
uint32_t idx = PA_INVALID_INDEX;
|
|
|
|
|
int64_t offset;
|
|
|
|
|
pa_card *card = NULL;
|
|
|
|
|
pa_device_port *port = NULL;
|
|
|
|
|
|
|
|
|
|
pa_native_connection_assert_ref(c);
|
|
|
|
|
pa_assert(t);
|
|
|
|
|
|
|
|
|
|
if (pa_tagstruct_getu32(t, &idx) < 0 ||
|
|
|
|
|
pa_tagstruct_gets(t, &card_name) < 0 ||
|
|
|
|
|
pa_tagstruct_gets(t, &port_name) < 0 ||
|
|
|
|
|
pa_tagstruct_gets64(t, &offset) < 0 ||
|
|
|
|
|
!pa_tagstruct_eof(t)) {
|
|
|
|
|
protocol_error(c);
|
2013-12-16 17:11:07 +01:00
|
|
|
return;
|
2012-06-28 15:00:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
|
|
|
|
|
CHECK_VALIDITY(c->pstream, !card_name || pa_namereg_is_valid_name(card_name), tag, PA_ERR_INVALID);
|
2013-11-19 11:59:39 -09:00
|
|
|
CHECK_VALIDITY(c->pstream, (idx != PA_INVALID_INDEX) ^ (card_name != NULL), tag, PA_ERR_INVALID);
|
2012-06-28 15:00:45 +02:00
|
|
|
CHECK_VALIDITY(c->pstream, port_name, tag, PA_ERR_INVALID);
|
|
|
|
|
|
|
|
|
|
if (idx != PA_INVALID_INDEX)
|
|
|
|
|
card = pa_idxset_get_by_index(c->protocol->core->cards, idx);
|
|
|
|
|
else
|
|
|
|
|
card = pa_namereg_get(c->protocol->core, card_name, PA_NAMEREG_CARD);
|
|
|
|
|
|
|
|
|
|
CHECK_VALIDITY(c->pstream, card, tag, PA_ERR_NOENTITY);
|
|
|
|
|
|
|
|
|
|
port = pa_hashmap_get(card->ports, port_name);
|
|
|
|
|
CHECK_VALIDITY(c->pstream, port, tag, PA_ERR_NOENTITY);
|
|
|
|
|
|
|
|
|
|
pa_device_port_set_latency_offset(port, offset);
|
|
|
|
|
|
|
|
|
|
pa_pstream_send_simple_ack(c->pstream, tag);
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-03 00:08:06 +02:00
|
|
|
static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
|
|
|
|
|
[PA_COMMAND_ERROR] = NULL,
|
|
|
|
|
[PA_COMMAND_TIMEOUT] = NULL,
|
|
|
|
|
[PA_COMMAND_REPLY] = NULL,
|
|
|
|
|
[PA_COMMAND_CREATE_PLAYBACK_STREAM] = command_create_playback_stream,
|
|
|
|
|
[PA_COMMAND_DELETE_PLAYBACK_STREAM] = command_delete_stream,
|
|
|
|
|
[PA_COMMAND_DRAIN_PLAYBACK_STREAM] = command_drain_playback_stream,
|
|
|
|
|
[PA_COMMAND_CREATE_RECORD_STREAM] = command_create_record_stream,
|
|
|
|
|
[PA_COMMAND_DELETE_RECORD_STREAM] = command_delete_stream,
|
|
|
|
|
[PA_COMMAND_AUTH] = command_auth,
|
|
|
|
|
[PA_COMMAND_REQUEST] = NULL,
|
|
|
|
|
[PA_COMMAND_EXIT] = command_exit,
|
|
|
|
|
[PA_COMMAND_SET_CLIENT_NAME] = command_set_client_name,
|
|
|
|
|
[PA_COMMAND_LOOKUP_SINK] = command_lookup,
|
|
|
|
|
[PA_COMMAND_LOOKUP_SOURCE] = command_lookup,
|
|
|
|
|
[PA_COMMAND_STAT] = command_stat,
|
|
|
|
|
[PA_COMMAND_GET_PLAYBACK_LATENCY] = command_get_playback_latency,
|
|
|
|
|
[PA_COMMAND_GET_RECORD_LATENCY] = command_get_record_latency,
|
|
|
|
|
[PA_COMMAND_CREATE_UPLOAD_STREAM] = command_create_upload_stream,
|
|
|
|
|
[PA_COMMAND_DELETE_UPLOAD_STREAM] = command_delete_stream,
|
|
|
|
|
[PA_COMMAND_FINISH_UPLOAD_STREAM] = command_finish_upload_stream,
|
|
|
|
|
[PA_COMMAND_PLAY_SAMPLE] = command_play_sample,
|
|
|
|
|
[PA_COMMAND_REMOVE_SAMPLE] = command_remove_sample,
|
|
|
|
|
[PA_COMMAND_GET_SINK_INFO] = command_get_info,
|
|
|
|
|
[PA_COMMAND_GET_SOURCE_INFO] = command_get_info,
|
|
|
|
|
[PA_COMMAND_GET_CLIENT_INFO] = command_get_info,
|
|
|
|
|
[PA_COMMAND_GET_CARD_INFO] = command_get_info,
|
|
|
|
|
[PA_COMMAND_GET_MODULE_INFO] = command_get_info,
|
|
|
|
|
[PA_COMMAND_GET_SINK_INPUT_INFO] = command_get_info,
|
|
|
|
|
[PA_COMMAND_GET_SOURCE_OUTPUT_INFO] = command_get_info,
|
|
|
|
|
[PA_COMMAND_GET_SAMPLE_INFO] = command_get_info,
|
|
|
|
|
[PA_COMMAND_GET_SINK_INFO_LIST] = command_get_info_list,
|
|
|
|
|
[PA_COMMAND_GET_SOURCE_INFO_LIST] = command_get_info_list,
|
|
|
|
|
[PA_COMMAND_GET_MODULE_INFO_LIST] = command_get_info_list,
|
|
|
|
|
[PA_COMMAND_GET_CLIENT_INFO_LIST] = command_get_info_list,
|
|
|
|
|
[PA_COMMAND_GET_CARD_INFO_LIST] = command_get_info_list,
|
|
|
|
|
[PA_COMMAND_GET_SINK_INPUT_INFO_LIST] = command_get_info_list,
|
|
|
|
|
[PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST] = command_get_info_list,
|
|
|
|
|
[PA_COMMAND_GET_SAMPLE_INFO_LIST] = command_get_info_list,
|
|
|
|
|
[PA_COMMAND_GET_SERVER_INFO] = command_get_server_info,
|
|
|
|
|
[PA_COMMAND_SUBSCRIBE] = command_subscribe,
|
|
|
|
|
|
|
|
|
|
[PA_COMMAND_SET_SINK_VOLUME] = command_set_volume,
|
|
|
|
|
[PA_COMMAND_SET_SINK_INPUT_VOLUME] = command_set_volume,
|
|
|
|
|
[PA_COMMAND_SET_SOURCE_VOLUME] = command_set_volume,
|
|
|
|
|
[PA_COMMAND_SET_SOURCE_OUTPUT_VOLUME] = command_set_volume,
|
|
|
|
|
|
|
|
|
|
[PA_COMMAND_SET_SINK_MUTE] = command_set_mute,
|
|
|
|
|
[PA_COMMAND_SET_SINK_INPUT_MUTE] = command_set_mute,
|
|
|
|
|
[PA_COMMAND_SET_SOURCE_MUTE] = command_set_mute,
|
|
|
|
|
[PA_COMMAND_SET_SOURCE_OUTPUT_MUTE] = command_set_mute,
|
|
|
|
|
|
|
|
|
|
[PA_COMMAND_SUSPEND_SINK] = command_suspend,
|
|
|
|
|
[PA_COMMAND_SUSPEND_SOURCE] = command_suspend,
|
|
|
|
|
|
|
|
|
|
[PA_COMMAND_CORK_PLAYBACK_STREAM] = command_cork_playback_stream,
|
|
|
|
|
[PA_COMMAND_FLUSH_PLAYBACK_STREAM] = command_trigger_or_flush_or_prebuf_playback_stream,
|
|
|
|
|
[PA_COMMAND_TRIGGER_PLAYBACK_STREAM] = command_trigger_or_flush_or_prebuf_playback_stream,
|
|
|
|
|
[PA_COMMAND_PREBUF_PLAYBACK_STREAM] = command_trigger_or_flush_or_prebuf_playback_stream,
|
|
|
|
|
|
|
|
|
|
[PA_COMMAND_CORK_RECORD_STREAM] = command_cork_record_stream,
|
|
|
|
|
[PA_COMMAND_FLUSH_RECORD_STREAM] = command_flush_record_stream,
|
|
|
|
|
|
|
|
|
|
[PA_COMMAND_SET_DEFAULT_SINK] = command_set_default_sink_or_source,
|
|
|
|
|
[PA_COMMAND_SET_DEFAULT_SOURCE] = command_set_default_sink_or_source,
|
|
|
|
|
[PA_COMMAND_SET_PLAYBACK_STREAM_NAME] = command_set_stream_name,
|
|
|
|
|
[PA_COMMAND_SET_RECORD_STREAM_NAME] = command_set_stream_name,
|
|
|
|
|
[PA_COMMAND_KILL_CLIENT] = command_kill,
|
|
|
|
|
[PA_COMMAND_KILL_SINK_INPUT] = command_kill,
|
|
|
|
|
[PA_COMMAND_KILL_SOURCE_OUTPUT] = command_kill,
|
|
|
|
|
[PA_COMMAND_LOAD_MODULE] = command_load_module,
|
|
|
|
|
[PA_COMMAND_UNLOAD_MODULE] = command_unload_module,
|
|
|
|
|
|
|
|
|
|
[PA_COMMAND_GET_AUTOLOAD_INFO___OBSOLETE] = NULL,
|
|
|
|
|
[PA_COMMAND_GET_AUTOLOAD_INFO_LIST___OBSOLETE] = NULL,
|
|
|
|
|
[PA_COMMAND_ADD_AUTOLOAD___OBSOLETE] = NULL,
|
|
|
|
|
[PA_COMMAND_REMOVE_AUTOLOAD___OBSOLETE] = NULL,
|
|
|
|
|
|
|
|
|
|
[PA_COMMAND_MOVE_SINK_INPUT] = command_move_stream,
|
|
|
|
|
[PA_COMMAND_MOVE_SOURCE_OUTPUT] = command_move_stream,
|
|
|
|
|
|
|
|
|
|
[PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR] = command_set_stream_buffer_attr,
|
|
|
|
|
[PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR] = command_set_stream_buffer_attr,
|
|
|
|
|
|
|
|
|
|
[PA_COMMAND_UPDATE_PLAYBACK_STREAM_SAMPLE_RATE] = command_update_stream_sample_rate,
|
|
|
|
|
[PA_COMMAND_UPDATE_RECORD_STREAM_SAMPLE_RATE] = command_update_stream_sample_rate,
|
|
|
|
|
|
|
|
|
|
[PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST] = command_update_proplist,
|
|
|
|
|
[PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST] = command_update_proplist,
|
|
|
|
|
[PA_COMMAND_UPDATE_CLIENT_PROPLIST] = command_update_proplist,
|
|
|
|
|
|
|
|
|
|
[PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST] = command_remove_proplist,
|
|
|
|
|
[PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST] = command_remove_proplist,
|
|
|
|
|
[PA_COMMAND_REMOVE_CLIENT_PROPLIST] = command_remove_proplist,
|
|
|
|
|
|
|
|
|
|
[PA_COMMAND_SET_CARD_PROFILE] = command_set_card_profile,
|
|
|
|
|
|
|
|
|
|
[PA_COMMAND_SET_SINK_PORT] = command_set_sink_or_source_port,
|
|
|
|
|
[PA_COMMAND_SET_SOURCE_PORT] = command_set_sink_or_source_port,
|
|
|
|
|
|
|
|
|
|
[PA_COMMAND_SET_PORT_LATENCY_OFFSET] = command_set_port_latency_offset,
|
|
|
|
|
|
|
|
|
|
[PA_COMMAND_ENABLE_SRBCHANNEL] = command_enable_srbchannel,
|
|
|
|
|
|
|
|
|
|
[PA_COMMAND_REGISTER_MEMFD_SHMID] = command_register_memfd_shmid,
|
|
|
|
|
|
2020-01-14 10:15:36 +01:00
|
|
|
[PA_COMMAND_SEND_OBJECT_MESSAGE] = command_send_object_message,
|
|
|
|
|
|
2016-07-03 00:08:06 +02:00
|
|
|
[PA_COMMAND_EXTENSION] = command_extension
|
|
|
|
|
};
|
|
|
|
|
|
2004-06-23 23:17:30 +00:00
|
|
|
/*** pstream callbacks ***/
|
|
|
|
|
|
2016-03-13 01:12:18 +02:00
|
|
|
static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, pa_cmsg_ancil_data *ancil_data, void *userdata) {
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
2007-10-28 19:13:50 +00:00
|
|
|
|
|
|
|
|
pa_assert(p);
|
|
|
|
|
pa_assert(packet);
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_assert_ref(c);
|
2004-06-20 01:12:13 +00:00
|
|
|
|
2014-08-06 07:48:19 +05:30
|
|
|
if (pa_pdispatch_run(c->pdispatch, packet, ancil_data, c) < 0) {
|
2006-08-18 21:38:40 +00:00
|
|
|
pa_log("invalid packet.");
|
2008-08-04 18:58:29 +02:00
|
|
|
native_connection_unlink(c);
|
2004-06-23 23:17:30 +00:00
|
|
|
}
|
2004-06-20 01:12:13 +00:00
|
|
|
}
|
|
|
|
|
|
2006-02-20 04:05:16 +00:00
|
|
|
static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk, void *userdata) {
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
2007-10-28 19:13:50 +00:00
|
|
|
output_stream *stream;
|
|
|
|
|
|
|
|
|
|
pa_assert(p);
|
|
|
|
|
pa_assert(chunk);
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_assert_ref(c);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
if (!(stream = OUTPUT_STREAM(pa_idxset_get_by_index(c->output_streams, channel)))) {
|
2009-04-10 01:16:59 +02:00
|
|
|
pa_log_debug("Client sent block for invalid stream.");
|
2006-08-13 17:31:58 +00:00
|
|
|
/* Ignoring */
|
2004-07-07 00:22:46 +00:00
|
|
|
return;
|
2004-06-20 01:12:13 +00:00
|
|
|
}
|
|
|
|
|
|
2011-11-10 12:43:09 +05:30
|
|
|
#ifdef PROTOCOL_NATIVE_DEBUG
|
|
|
|
|
pa_log("got %lu bytes from client", (unsigned long) chunk->length);
|
|
|
|
|
#endif
|
2008-09-03 18:31:46 +02:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
if (playback_stream_isinstance(stream)) {
|
|
|
|
|
playback_stream *ps = PLAYBACK_STREAM(stream);
|
2006-02-20 04:05:16 +00:00
|
|
|
|
2014-10-16 11:41:19 +02:00
|
|
|
size_t frame_size = pa_frame_size(&ps->sink_input->sample_spec);
|
|
|
|
|
if (chunk->index % frame_size != 0 || chunk->length % frame_size != 0) {
|
|
|
|
|
pa_log_warn("Client sent non-aligned memblock: index %d, length %d, frame size: %d",
|
|
|
|
|
(int) chunk->index, (int) chunk->length, (int) frame_size);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-09 11:08:37 +01:00
|
|
|
pa_atomic_inc(&ps->seek_or_post_in_queue);
|
2009-02-18 21:55:55 +01:00
|
|
|
if (chunk->memblock) {
|
|
|
|
|
if (seek != PA_SEEK_RELATIVE || offset != 0)
|
2010-12-06 16:25:25 +01:00
|
|
|
pa_asyncmsgq_post(ps->sink_input->sink->asyncmsgq, PA_MSGOBJECT(ps->sink_input), SINK_INPUT_MESSAGE_SEEK, PA_UINT_TO_PTR(seek), offset, chunk, NULL);
|
|
|
|
|
else
|
|
|
|
|
pa_asyncmsgq_post(ps->sink_input->sink->asyncmsgq, PA_MSGOBJECT(ps->sink_input), SINK_INPUT_MESSAGE_POST_DATA, NULL, 0, chunk, NULL);
|
2009-02-18 21:55:55 +01:00
|
|
|
} else
|
|
|
|
|
pa_asyncmsgq_post(ps->sink_input->sink->asyncmsgq, PA_MSGOBJECT(ps->sink_input), SINK_INPUT_MESSAGE_SEEK, PA_UINT_TO_PTR(seek), offset+chunk->length, NULL, NULL);
|
2004-08-27 01:29:49 +00:00
|
|
|
|
2004-08-03 19:26:56 +00:00
|
|
|
} else {
|
2007-10-28 19:13:50 +00:00
|
|
|
upload_stream *u = UPLOAD_STREAM(stream);
|
2004-08-03 19:26:56 +00:00
|
|
|
size_t l;
|
|
|
|
|
|
|
|
|
|
if (!u->memchunk.memblock) {
|
2009-02-18 21:55:55 +01:00
|
|
|
if (u->length == chunk->length && chunk->memblock) {
|
2004-08-03 19:26:56 +00:00
|
|
|
u->memchunk = *chunk;
|
|
|
|
|
pa_memblock_ref(u->memchunk.memblock);
|
|
|
|
|
u->length = 0;
|
|
|
|
|
} else {
|
2006-08-18 19:55:18 +00:00
|
|
|
u->memchunk.memblock = pa_memblock_new(c->protocol->core->mempool, u->length);
|
2004-08-03 19:26:56 +00:00
|
|
|
u->memchunk.index = u->memchunk.length = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(u->memchunk.memblock);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
|
|
|
|
l = u->length;
|
2004-08-03 19:26:56 +00:00
|
|
|
if (l > chunk->length)
|
|
|
|
|
l = chunk->length;
|
|
|
|
|
|
|
|
|
|
if (l > 0) {
|
2009-02-18 21:55:55 +01:00
|
|
|
void *dst;
|
2007-10-28 19:13:50 +00:00
|
|
|
dst = pa_memblock_acquire(u->memchunk.memblock);
|
|
|
|
|
|
2009-02-18 21:55:55 +01:00
|
|
|
if (chunk->memblock) {
|
|
|
|
|
void *src;
|
|
|
|
|
src = pa_memblock_acquire(chunk->memblock);
|
|
|
|
|
|
|
|
|
|
memcpy((uint8_t*) dst + u->memchunk.index + u->memchunk.length,
|
|
|
|
|
(uint8_t*) src + chunk->index, l);
|
|
|
|
|
|
|
|
|
|
pa_memblock_release(chunk->memblock);
|
|
|
|
|
} else
|
|
|
|
|
pa_silence_memory((uint8_t*) dst + u->memchunk.index + u->memchunk.length, l, &u->sample_spec);
|
2007-10-28 19:13:50 +00:00
|
|
|
|
|
|
|
|
pa_memblock_release(u->memchunk.memblock);
|
|
|
|
|
|
2004-08-03 19:26:56 +00:00
|
|
|
u->memchunk.length += l;
|
|
|
|
|
u->length -= l;
|
|
|
|
|
}
|
|
|
|
|
}
|
2004-06-20 01:12:13 +00:00
|
|
|
}
|
|
|
|
|
|
2006-01-11 01:17:39 +00:00
|
|
|
static void pstream_die_callback(pa_pstream *p, void *userdata) {
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
2004-06-20 01:12:13 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(p);
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_assert_ref(c);
|
2004-06-08 23:54:24 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
native_connection_unlink(c);
|
|
|
|
|
pa_log_info("Connection died.");
|
2007-10-28 19:13:50 +00:00
|
|
|
}
|
2004-07-10 16:50:22 +00:00
|
|
|
|
2006-01-11 01:17:39 +00:00
|
|
|
static void pstream_drain_callback(pa_pstream *p, void *userdata) {
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
2007-10-28 19:13:50 +00:00
|
|
|
|
|
|
|
|
pa_assert(p);
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_assert_ref(c);
|
2004-07-10 16:50:22 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
native_connection_send_memblock(c);
|
2004-07-10 16:50:22 +00:00
|
|
|
}
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
static void pstream_revoke_callback(pa_pstream *p, uint32_t block_id, void *userdata) {
|
|
|
|
|
pa_thread_mq *q;
|
|
|
|
|
|
|
|
|
|
if (!(q = pa_thread_mq_get()))
|
|
|
|
|
pa_pstream_send_revoke(p, block_id);
|
|
|
|
|
else
|
|
|
|
|
pa_asyncmsgq_post(q->outq, PA_MSGOBJECT(userdata), CONNECTION_MESSAGE_REVOKE, PA_UINT_TO_PTR(block_id), 0, NULL, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void pstream_release_callback(pa_pstream *p, uint32_t block_id, void *userdata) {
|
|
|
|
|
pa_thread_mq *q;
|
|
|
|
|
|
|
|
|
|
if (!(q = pa_thread_mq_get()))
|
|
|
|
|
pa_pstream_send_release(p, block_id);
|
|
|
|
|
else
|
|
|
|
|
pa_asyncmsgq_post(q->outq, PA_MSGOBJECT(userdata), CONNECTION_MESSAGE_RELEASE, PA_UINT_TO_PTR(block_id), 0, NULL, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
2004-07-06 00:08:44 +00:00
|
|
|
/*** client callbacks ***/
|
|
|
|
|
|
2006-01-11 01:17:39 +00:00
|
|
|
static void client_kill_cb(pa_client *c) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(c);
|
|
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
native_connection_unlink(PA_NATIVE_CONNECTION(c->userdata));
|
|
|
|
|
pa_log_info("Connection killed.");
|
2004-07-06 00:08:44 +00:00
|
|
|
}
|
|
|
|
|
|
2009-02-12 03:18:05 +01:00
|
|
|
static void client_send_event_cb(pa_client *client, const char*event, pa_proplist *pl) {
|
|
|
|
|
pa_tagstruct *t;
|
|
|
|
|
pa_native_connection *c;
|
|
|
|
|
|
|
|
|
|
pa_assert(client);
|
|
|
|
|
c = PA_NATIVE_CONNECTION(client->userdata);
|
|
|
|
|
pa_native_connection_assert_ref(c);
|
|
|
|
|
|
|
|
|
|
if (c->version < 15)
|
|
|
|
|
return;
|
|
|
|
|
|
2014-10-23 16:09:45 +02:00
|
|
|
t = pa_tagstruct_new();
|
2009-02-12 03:18:05 +01:00
|
|
|
pa_tagstruct_putu32(t, PA_COMMAND_CLIENT_EVENT);
|
|
|
|
|
pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
|
|
|
|
|
pa_tagstruct_puts(t, event);
|
|
|
|
|
pa_tagstruct_put_proplist(t, pl);
|
|
|
|
|
pa_pstream_send_tagstruct(c->pstream, t);
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-03 16:44:38 +02:00
|
|
|
/*** module entry points ***/
|
2004-06-20 01:12:13 +00:00
|
|
|
|
2009-04-05 02:13:43 +03:00
|
|
|
static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timeval *t, void *userdata) {
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
2007-10-28 19:13:50 +00:00
|
|
|
|
|
|
|
|
pa_assert(m);
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection_assert_ref(c);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(c->auth_timeout_event == e);
|
2004-11-18 00:28:26 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
if (!c->authorized) {
|
|
|
|
|
native_connection_unlink(c);
|
|
|
|
|
pa_log_info("Connection terminated due to authentication timeout.");
|
|
|
|
|
}
|
2004-11-18 00:28:26 +00:00
|
|
|
}
|
|
|
|
|
|
2008-08-03 16:44:38 +02:00
|
|
|
void pa_native_protocol_connect(pa_native_protocol *p, pa_iochannel *io, pa_native_options *o) {
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c;
|
2009-01-15 17:52:29 +01:00
|
|
|
char pname[128];
|
|
|
|
|
pa_client *client;
|
|
|
|
|
pa_client_new_data data;
|
2007-10-28 19:13:50 +00:00
|
|
|
|
|
|
|
|
pa_assert(p);
|
2008-08-03 16:44:38 +02:00
|
|
|
pa_assert(io);
|
|
|
|
|
pa_assert(o);
|
2004-06-20 01:12:13 +00:00
|
|
|
|
2006-01-11 01:17:39 +00:00
|
|
|
if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
|
2006-08-18 21:38:40 +00:00
|
|
|
pa_log_warn("Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS);
|
2004-11-18 00:28:26 +00:00
|
|
|
pa_iochannel_free(io);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2009-01-15 17:52:29 +01:00
|
|
|
pa_client_new_data_init(&data);
|
|
|
|
|
data.module = o->module;
|
|
|
|
|
data.driver = __FILE__;
|
|
|
|
|
pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname));
|
|
|
|
|
pa_proplist_setf(data.proplist, PA_PROP_APPLICATION_NAME, "Native client (%s)", pname);
|
|
|
|
|
pa_proplist_sets(data.proplist, "native-protocol.peer", pname);
|
|
|
|
|
client = pa_client_new(p->core, &data);
|
|
|
|
|
pa_client_new_data_done(&data);
|
|
|
|
|
|
|
|
|
|
if (!client)
|
|
|
|
|
return;
|
|
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
c = pa_msgobject_new(pa_native_connection);
|
|
|
|
|
c->parent.parent.free = native_connection_free;
|
|
|
|
|
c->parent.process_msg = native_connection_process_msg;
|
2008-08-03 16:44:38 +02:00
|
|
|
c->protocol = p;
|
|
|
|
|
c->options = pa_native_options_ref(o);
|
2013-06-27 19:28:09 +02:00
|
|
|
c->authorized = false;
|
2014-04-25 17:57:18 +02:00
|
|
|
c->srbpending = NULL;
|
2008-08-03 16:44:38 +02:00
|
|
|
|
|
|
|
|
if (o->auth_anonymous) {
|
|
|
|
|
pa_log_info("Client authenticated anonymously.");
|
2013-06-27 19:28:09 +02:00
|
|
|
c->authorized = true;
|
2008-08-03 16:44:38 +02:00
|
|
|
}
|
2004-09-14 23:08:39 +00:00
|
|
|
|
2008-08-03 16:44:38 +02:00
|
|
|
if (!c->authorized &&
|
|
|
|
|
o->auth_ip_acl &&
|
|
|
|
|
pa_ip_acl_check(o->auth_ip_acl, pa_iochannel_get_recv_fd(io)) > 0) {
|
2004-11-18 00:28:26 +00:00
|
|
|
|
2006-08-18 21:38:40 +00:00
|
|
|
pa_log_info("Client authenticated by IP ACL.");
|
2013-06-27 19:28:09 +02:00
|
|
|
c->authorized = true;
|
2006-07-20 18:43:20 +00:00
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2009-04-05 02:13:43 +03:00
|
|
|
if (!c->authorized)
|
|
|
|
|
c->auth_timeout_event = pa_core_rttime_new(p->core, pa_rtclock_now() + AUTH_TIMEOUT, auth_timeout, c);
|
|
|
|
|
else
|
2004-11-18 00:28:26 +00:00
|
|
|
c->auth_timeout_event = NULL;
|
2006-05-25 23:20:28 +00:00
|
|
|
|
2008-06-17 18:29:00 +00:00
|
|
|
c->is_local = pa_iochannel_socket_is_local(io);
|
2006-05-25 23:20:28 +00:00
|
|
|
c->version = 8;
|
2008-08-03 16:44:38 +02:00
|
|
|
|
2009-01-15 17:52:29 +01:00
|
|
|
c->client = client;
|
2004-07-06 00:08:44 +00:00
|
|
|
c->client->kill = client_kill_cb;
|
2009-02-12 03:18:05 +01:00
|
|
|
c->client->send_event = client_send_event_cb;
|
2004-07-06 00:08:44 +00:00
|
|
|
c->client->userdata = c;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2016-03-13 00:53:34 +02:00
|
|
|
c->rw_mempool = NULL;
|
|
|
|
|
|
2006-08-18 19:55:18 +00:00
|
|
|
c->pstream = pa_pstream_new(p->core->mainloop, io, p->core->mempool);
|
2011-12-12 22:36:39 +00:00
|
|
|
pa_pstream_set_receive_packet_callback(c->pstream, pstream_packet_callback, c);
|
|
|
|
|
pa_pstream_set_receive_memblock_callback(c->pstream, pstream_memblock_callback, c);
|
2004-07-10 16:50:22 +00:00
|
|
|
pa_pstream_set_die_callback(c->pstream, pstream_die_callback, c);
|
|
|
|
|
pa_pstream_set_drain_callback(c->pstream, pstream_drain_callback, c);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_pstream_set_revoke_callback(c->pstream, pstream_revoke_callback, c);
|
|
|
|
|
pa_pstream_set_release_callback(c->pstream, pstream_release_callback, c);
|
2004-06-20 01:12:13 +00:00
|
|
|
|
2013-06-27 19:28:09 +02:00
|
|
|
c->pdispatch = pa_pdispatch_new(p->core->mainloop, true, command_table, PA_COMMAND_MAX);
|
2004-06-23 23:17:30 +00:00
|
|
|
|
2004-07-03 23:35:12 +00:00
|
|
|
c->record_streams = pa_idxset_new(NULL, NULL);
|
2004-08-03 19:26:56 +00:00
|
|
|
c->output_streams = pa_idxset_new(NULL, NULL);
|
2004-06-20 01:12:13 +00:00
|
|
|
|
2004-07-10 16:50:22 +00:00
|
|
|
c->rrobin_index = PA_IDXSET_INVALID;
|
2004-08-11 15:11:26 +00:00
|
|
|
c->subscription = NULL;
|
2004-07-10 16:50:22 +00:00
|
|
|
|
2004-07-03 23:35:12 +00:00
|
|
|
pa_idxset_put(p->connections, c, NULL);
|
2006-02-24 15:12:42 +00:00
|
|
|
|
2006-07-19 21:48:35 +00:00
|
|
|
#ifdef HAVE_CREDS
|
2006-02-24 15:12:42 +00:00
|
|
|
if (pa_iochannel_creds_supported(io))
|
|
|
|
|
pa_iochannel_creds_enable(io);
|
|
|
|
|
#endif
|
2008-08-04 18:58:29 +02:00
|
|
|
|
|
|
|
|
pa_hook_fire(&p->hooks[PA_NATIVE_HOOK_CONNECTION_PUT], c);
|
2004-06-20 01:12:13 +00:00
|
|
|
}
|
|
|
|
|
|
2008-08-03 16:44:38 +02:00
|
|
|
void pa_native_protocol_disconnect(pa_native_protocol *p, pa_module *m) {
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c;
|
2008-08-03 16:44:38 +02:00
|
|
|
void *state = NULL;
|
2004-06-20 01:12:13 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(p);
|
2008-08-03 16:44:38 +02:00
|
|
|
pa_assert(m);
|
2004-11-07 20:48:46 +00:00
|
|
|
|
2008-08-03 16:44:38 +02:00
|
|
|
while ((c = pa_idxset_iterate(p->connections, &state, NULL)))
|
|
|
|
|
if (c->options->module == m)
|
2008-08-04 18:58:29 +02:00
|
|
|
native_connection_unlink(c);
|
2008-08-03 16:44:38 +02:00
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-08-03 16:44:38 +02:00
|
|
|
static pa_native_protocol* native_protocol_new(pa_core *c) {
|
|
|
|
|
pa_native_protocol *p;
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_hook_t h;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-08-03 16:44:38 +02:00
|
|
|
pa_assert(c);
|
2004-11-07 20:48:46 +00:00
|
|
|
|
2008-08-03 16:44:38 +02:00
|
|
|
p = pa_xnew(pa_native_protocol, 1);
|
|
|
|
|
PA_REFCNT_INIT(p);
|
|
|
|
|
p->core = c;
|
|
|
|
|
p->connections = pa_idxset_new(NULL, NULL);
|
2004-11-07 20:48:46 +00:00
|
|
|
|
2008-08-03 16:44:38 +02:00
|
|
|
p->servers = NULL;
|
2008-08-04 18:58:29 +02:00
|
|
|
|
|
|
|
|
p->extensions = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
|
|
|
|
|
|
|
|
|
|
for (h = 0; h < PA_NATIVE_HOOK_MAX; h++)
|
|
|
|
|
pa_hook_init(&p->hooks[h], p);
|
2004-11-07 20:48:46 +00:00
|
|
|
|
2008-08-03 16:44:38 +02:00
|
|
|
pa_assert_se(pa_shared_set(c, "native-protocol", p) >= 0);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-08-03 16:44:38 +02:00
|
|
|
return p;
|
2004-11-07 20:48:46 +00:00
|
|
|
}
|
|
|
|
|
|
2008-08-03 16:44:38 +02:00
|
|
|
pa_native_protocol* pa_native_protocol_get(pa_core *c) {
|
|
|
|
|
pa_native_protocol *p;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-08-03 16:44:38 +02:00
|
|
|
if ((p = pa_shared_get(c, "native-protocol")))
|
|
|
|
|
return pa_native_protocol_ref(p);
|
2004-06-08 23:54:24 +00:00
|
|
|
|
2008-08-03 16:44:38 +02:00
|
|
|
return native_protocol_new(c);
|
|
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-08-03 16:44:38 +02:00
|
|
|
pa_native_protocol* pa_native_protocol_ref(pa_native_protocol *p) {
|
|
|
|
|
pa_assert(p);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(p) >= 1);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-08-03 16:44:38 +02:00
|
|
|
PA_REFCNT_INC(p);
|
2006-07-19 21:48:35 +00:00
|
|
|
|
2008-08-03 16:44:38 +02:00
|
|
|
return p;
|
|
|
|
|
}
|
2006-07-20 18:43:20 +00:00
|
|
|
|
2008-08-03 16:44:38 +02:00
|
|
|
void pa_native_protocol_unref(pa_native_protocol *p) {
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_native_connection *c;
|
|
|
|
|
pa_native_hook_t h;
|
|
|
|
|
|
2008-08-03 16:44:38 +02:00
|
|
|
pa_assert(p);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(p) >= 1);
|
2006-07-20 18:43:20 +00:00
|
|
|
|
2008-08-03 16:44:38 +02:00
|
|
|
if (PA_REFCNT_DEC(p) > 0)
|
|
|
|
|
return;
|
2006-07-20 18:43:20 +00:00
|
|
|
|
2008-08-03 16:44:38 +02:00
|
|
|
while ((c = pa_idxset_first(p->connections, NULL)))
|
2008-08-04 18:58:29 +02:00
|
|
|
native_connection_unlink(c);
|
2004-07-10 20:56:38 +00:00
|
|
|
|
2013-02-12 21:36:55 +02:00
|
|
|
pa_idxset_free(p->connections, NULL);
|
2006-07-20 18:43:20 +00:00
|
|
|
|
2008-08-03 16:44:38 +02:00
|
|
|
pa_strlist_free(p->servers);
|
2008-08-04 18:58:29 +02:00
|
|
|
|
|
|
|
|
for (h = 0; h < PA_NATIVE_HOOK_MAX; h++)
|
|
|
|
|
pa_hook_done(&p->hooks[h]);
|
|
|
|
|
|
2013-09-14 11:50:10 +05:30
|
|
|
pa_hashmap_free(p->extensions);
|
2004-06-08 23:54:24 +00:00
|
|
|
|
2008-08-03 16:44:38 +02:00
|
|
|
pa_assert_se(pa_shared_remove(p->core, "native-protocol") >= 0);
|
2006-07-20 18:43:20 +00:00
|
|
|
|
|
|
|
|
pa_xfree(p);
|
2004-09-01 21:12:27 +00:00
|
|
|
}
|
|
|
|
|
|
2008-08-03 16:44:38 +02:00
|
|
|
void pa_native_protocol_add_server_string(pa_native_protocol *p, const char *name) {
|
|
|
|
|
pa_assert(p);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(p) >= 1);
|
|
|
|
|
pa_assert(name);
|
2004-09-01 21:12:27 +00:00
|
|
|
|
2008-08-03 16:44:38 +02:00
|
|
|
p->servers = pa_strlist_prepend(p->servers, name);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_hook_fire(&p->hooks[PA_NATIVE_HOOK_SERVERS_CHANGED], p->servers);
|
2008-08-03 16:44:38 +02:00
|
|
|
}
|
2004-11-11 21:18:33 +00:00
|
|
|
|
2008-08-03 16:44:38 +02:00
|
|
|
void pa_native_protocol_remove_server_string(pa_native_protocol *p, const char *name) {
|
|
|
|
|
pa_assert(p);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(p) >= 1);
|
|
|
|
|
pa_assert(name);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-08-03 16:44:38 +02:00
|
|
|
p->servers = pa_strlist_remove(p->servers, name);
|
|
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_hook_fire(&p->hooks[PA_NATIVE_HOOK_SERVERS_CHANGED], p->servers);
|
2004-06-08 23:54:24 +00:00
|
|
|
}
|
|
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
pa_hook *pa_native_protocol_hooks(pa_native_protocol *p) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(p);
|
2008-08-03 16:44:38 +02:00
|
|
|
pa_assert(PA_REFCNT_VALUE(p) >= 1);
|
2004-06-08 23:54:24 +00:00
|
|
|
|
2008-08-04 18:58:29 +02:00
|
|
|
return p->hooks;
|
2008-08-03 16:44:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pa_strlist *pa_native_protocol_servers(pa_native_protocol *p) {
|
|
|
|
|
pa_assert(p);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(p) >= 1);
|
2004-09-01 21:12:27 +00:00
|
|
|
|
2008-08-03 16:44:38 +02:00
|
|
|
return p->servers;
|
|
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-08-03 18:56:10 +02:00
|
|
|
int pa_native_protocol_install_ext(pa_native_protocol *p, pa_module *m, pa_native_protocol_ext_cb_t cb) {
|
|
|
|
|
pa_assert(p);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(p) >= 1);
|
|
|
|
|
pa_assert(m);
|
|
|
|
|
pa_assert(cb);
|
|
|
|
|
pa_assert(!pa_hashmap_get(p->extensions, m));
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2009-01-22 00:22:46 +01:00
|
|
|
pa_assert_se(pa_hashmap_put(p->extensions, m, (void*) (unsigned long) cb) == 0);
|
2008-08-03 18:56:10 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
2008-08-03 16:44:38 +02:00
|
|
|
|
2008-08-03 18:56:10 +02:00
|
|
|
void pa_native_protocol_remove_ext(pa_native_protocol *p, pa_module *m) {
|
|
|
|
|
pa_assert(p);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(p) >= 1);
|
|
|
|
|
pa_assert(m);
|
2008-08-03 16:44:38 +02:00
|
|
|
|
2008-08-03 18:56:10 +02:00
|
|
|
pa_assert_se(pa_hashmap_remove(p->extensions, m));
|
|
|
|
|
}
|
2008-08-03 16:44:38 +02:00
|
|
|
|
|
|
|
|
pa_native_options* pa_native_options_new(void) {
|
|
|
|
|
pa_native_options *o;
|
|
|
|
|
|
|
|
|
|
o = pa_xnew0(pa_native_options, 1);
|
|
|
|
|
PA_REFCNT_INIT(o);
|
|
|
|
|
|
|
|
|
|
return o;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pa_native_options* pa_native_options_ref(pa_native_options *o) {
|
|
|
|
|
pa_assert(o);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(o) >= 1);
|
|
|
|
|
|
|
|
|
|
PA_REFCNT_INC(o);
|
|
|
|
|
|
|
|
|
|
return o;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void pa_native_options_unref(pa_native_options *o) {
|
|
|
|
|
pa_assert(o);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(o) >= 1);
|
|
|
|
|
|
|
|
|
|
if (PA_REFCNT_DEC(o) > 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
pa_xfree(o->auth_group);
|
|
|
|
|
|
|
|
|
|
if (o->auth_ip_acl)
|
|
|
|
|
pa_ip_acl_free(o->auth_ip_acl);
|
|
|
|
|
|
|
|
|
|
if (o->auth_cookie)
|
|
|
|
|
pa_auth_cookie_unref(o->auth_cookie);
|
|
|
|
|
|
|
|
|
|
pa_xfree(o);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int pa_native_options_parse(pa_native_options *o, pa_core *c, pa_modargs *ma) {
|
2013-06-27 19:28:09 +02:00
|
|
|
bool enabled;
|
2008-08-03 16:44:38 +02:00
|
|
|
const char *acl;
|
|
|
|
|
|
|
|
|
|
pa_assert(o);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(o) >= 1);
|
|
|
|
|
pa_assert(ma);
|
|
|
|
|
|
2015-03-05 14:55:52 +01:00
|
|
|
o->srbchannel = true;
|
2014-04-25 17:57:18 +02:00
|
|
|
if (pa_modargs_get_value_boolean(ma, "srbchannel", &o->srbchannel) < 0) {
|
|
|
|
|
pa_log("srbchannel= expects a boolean argument.");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-03 16:44:38 +02:00
|
|
|
if (pa_modargs_get_value_boolean(ma, "auth-anonymous", &o->auth_anonymous) < 0) {
|
|
|
|
|
pa_log("auth-anonymous= expects a boolean argument.");
|
|
|
|
|
return -1;
|
2004-11-11 21:18:33 +00:00
|
|
|
}
|
2004-11-07 20:48:46 +00:00
|
|
|
|
2013-06-27 19:28:09 +02:00
|
|
|
enabled = true;
|
2011-12-14 20:48:12 +05:30
|
|
|
if (pa_modargs_get_value_boolean(ma, "auth-group-enable", &enabled) < 0) {
|
|
|
|
|
pa_log("auth-group-enable= expects a boolean argument.");
|
2008-08-03 16:44:38 +02:00
|
|
|
return -1;
|
|
|
|
|
}
|
2004-11-11 21:18:33 +00:00
|
|
|
|
2008-08-03 16:44:38 +02:00
|
|
|
pa_xfree(o->auth_group);
|
|
|
|
|
o->auth_group = enabled ? pa_xstrdup(pa_modargs_get_value(ma, "auth-group", pa_in_system_mode() ? PA_ACCESS_GROUP : NULL)) : NULL;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-08-03 16:44:38 +02:00
|
|
|
#ifndef HAVE_CREDS
|
|
|
|
|
if (o->auth_group)
|
|
|
|
|
pa_log_warn("Authentication group configured, but not available on local system. Ignoring.");
|
2006-02-24 17:14:23 +00:00
|
|
|
#endif
|
2004-09-01 21:12:27 +00:00
|
|
|
|
2008-08-03 16:44:38 +02:00
|
|
|
if ((acl = pa_modargs_get_value(ma, "auth-ip-acl", NULL))) {
|
|
|
|
|
pa_ip_acl *ipa;
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2008-08-11 19:46:28 +02:00
|
|
|
if (!(ipa = pa_ip_acl_new(acl))) {
|
2008-08-03 16:44:38 +02:00
|
|
|
pa_log("Failed to parse IP ACL '%s'", acl);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2004-09-01 21:12:27 +00:00
|
|
|
|
2008-08-03 16:44:38 +02:00
|
|
|
if (o->auth_ip_acl)
|
|
|
|
|
pa_ip_acl_free(o->auth_ip_acl);
|
2004-09-01 21:12:27 +00:00
|
|
|
|
2008-08-03 16:44:38 +02:00
|
|
|
o->auth_ip_acl = ipa;
|
|
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2013-06-27 19:28:09 +02:00
|
|
|
enabled = true;
|
2008-08-03 16:44:38 +02:00
|
|
|
if (pa_modargs_get_value_boolean(ma, "auth-cookie-enabled", &enabled) < 0) {
|
|
|
|
|
pa_log("auth-cookie-enabled= expects a boolean argument.");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (o->auth_cookie)
|
|
|
|
|
pa_auth_cookie_unref(o->auth_cookie);
|
|
|
|
|
|
|
|
|
|
if (enabled) {
|
|
|
|
|
const char *cn;
|
|
|
|
|
|
|
|
|
|
/* The new name for this is 'auth-cookie', for compat reasons
|
|
|
|
|
* we check the old name too */
|
2012-05-15 23:59:33 +02:00
|
|
|
cn = pa_modargs_get_value(ma, "auth-cookie", NULL);
|
|
|
|
|
if (!cn)
|
|
|
|
|
cn = pa_modargs_get_value(ma, "cookie", NULL);
|
2008-08-03 16:44:38 +02:00
|
|
|
|
2012-05-15 23:59:33 +02:00
|
|
|
if (cn)
|
2013-06-27 19:28:09 +02:00
|
|
|
o->auth_cookie = pa_auth_cookie_get(c, cn, true, PA_NATIVE_COOKIE_LENGTH);
|
2012-05-15 23:59:33 +02:00
|
|
|
else {
|
2013-06-27 19:28:09 +02:00
|
|
|
o->auth_cookie = pa_auth_cookie_get(c, PA_NATIVE_COOKIE_FILE, false, PA_NATIVE_COOKIE_LENGTH);
|
2012-05-15 23:59:33 +02:00
|
|
|
if (!o->auth_cookie) {
|
2014-06-08 16:32:58 +03:00
|
|
|
char *fallback_path;
|
|
|
|
|
|
|
|
|
|
if (pa_append_to_home_dir(PA_NATIVE_COOKIE_FILE_FALLBACK, &fallback_path) >= 0) {
|
|
|
|
|
o->auth_cookie = pa_auth_cookie_get(c, fallback_path, false, PA_NATIVE_COOKIE_LENGTH);
|
|
|
|
|
pa_xfree(fallback_path);
|
|
|
|
|
}
|
2012-05-15 23:59:33 +02:00
|
|
|
|
|
|
|
|
if (!o->auth_cookie)
|
2013-06-27 19:28:09 +02:00
|
|
|
o->auth_cookie = pa_auth_cookie_get(c, PA_NATIVE_COOKIE_FILE, true, PA_NATIVE_COOKIE_LENGTH);
|
2012-05-15 23:59:33 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!o->auth_cookie)
|
2008-08-03 16:44:38 +02:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
} else
|
|
|
|
|
o->auth_cookie = NULL;
|
|
|
|
|
|
|
|
|
|
return 0;
|
2004-09-01 21:12:27 +00:00
|
|
|
}
|
2008-08-04 18:58:29 +02:00
|
|
|
|
|
|
|
|
pa_pstream* pa_native_connection_get_pstream(pa_native_connection *c) {
|
|
|
|
|
pa_native_connection_assert_ref(c);
|
|
|
|
|
|
|
|
|
|
return c->pstream;
|
|
|
|
|
}
|
2009-08-27 00:05:54 +02:00
|
|
|
|
|
|
|
|
pa_client* pa_native_connection_get_client(pa_native_connection *c) {
|
2011-03-02 12:41:26 +01:00
|
|
|
pa_native_connection_assert_ref(c);
|
2009-08-27 00:05:54 +02:00
|
|
|
|
2011-03-02 12:41:26 +01:00
|
|
|
return c->client;
|
2009-08-27 00:05:54 +02:00
|
|
|
}
|