mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-10-29 05:40:23 -04:00
esound protocol
git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@40 fefdeb5f-60dc-0310-8127-8f9354f1896f
This commit is contained in:
parent
a74cd2a1bd
commit
ef422fa4ae
18 changed files with 806 additions and 46 deletions
|
|
@ -29,7 +29,8 @@ pkglib_LTLIBRARIES=libiochannel.la libsocket-server.la libsocket-client.la \
|
|||
libprotocol-cli.la module-cli-protocol-unix.la libtagstruct.la \
|
||||
libpdispatch.la libprotocol-native.la libpstream-util.la \
|
||||
module-native-protocol-tcp.la module-native-protocol-unix.la \
|
||||
libpolyp.la
|
||||
libpolyp.la libprotocol-esound.la module-esound-protocol-unix.la \
|
||||
module-esound-protocol-tcp.la
|
||||
|
||||
polypaudio_SOURCES = idxset.c idxset.h \
|
||||
queue.c queue.h \
|
||||
|
|
@ -113,40 +114,54 @@ libprotocol_cli_la_LIBADD = libsocket-server.la libiochannel.la libcli.la
|
|||
|
||||
libprotocol_native_la_SOURCES = protocol-native.c protocol-native.h
|
||||
libprotocol_native_la_LDFLAGS = -avoid-version
|
||||
libprotocol_native_la_LIBADD = libsocket-server.la libiochannel.la libpacket.la libpstream.la libpstream-util.la libpdispatch.la
|
||||
libprotocol_native_la_LIBADD = libsocket-server.la libiochannel.la libpacket.la libpstream.la libpstream-util.la libpdispatch.la libtagstruct.la
|
||||
|
||||
libtagstruct_la_SOURCES = tagstruct.c tagstruct.h
|
||||
libtagstruct_la_LDFLAGS = -avoid-version
|
||||
|
||||
libprotocol_esound_la_SOURCES = protocol-esound.c protocol-esound.h protocol-esound-spec.h
|
||||
libprotocol_esound_la_LDFLAGS = -avoid-version
|
||||
libprotocol_esound_la_LIBADD = libsocket-server.la libiochannel.la
|
||||
|
||||
module_simple_protocol_tcp_la_SOURCES = module-protocol-stub.c
|
||||
module_simple_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_SIMPLE $(AM_CFLAGS)
|
||||
module_simple_protocol_tcp_la_LDFLAGS = -module -avoid-version
|
||||
module_simple_protocol_tcp_la_LIBADD = libprotocol-simple.la libiochannel.la
|
||||
module_simple_protocol_tcp_la_LIBADD = libprotocol-simple.la libsocket-server.la
|
||||
|
||||
module_simple_protocol_unix_la_SOURCES = module-protocol-stub.c
|
||||
module_simple_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_SIMPLE $(AM_CFLAGS)
|
||||
module_simple_protocol_unix_la_LDFLAGS = -module -avoid-version
|
||||
module_simple_protocol_unix_la_LIBADD = libprotocol-simple.la libiochannel.la
|
||||
module_simple_protocol_unix_la_LIBADD = libprotocol-simple.la libsocket-server.la
|
||||
|
||||
module_cli_protocol_tcp_la_SOURCES = module-protocol-stub.c
|
||||
module_cli_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_CLI $(AM_CFLAGS)
|
||||
module_cli_protocol_tcp_la_LDFLAGS = -module -avoid-version
|
||||
module_cli_protocol_tcp_la_LIBADD = libprotocol-cli.la libiochannel.la
|
||||
module_cli_protocol_tcp_la_LIBADD = libprotocol-cli.la libsocket-server.la
|
||||
|
||||
module_cli_protocol_unix_la_SOURCES = module-protocol-stub.c
|
||||
module_cli_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_CLI $(AM_CFLAGS)
|
||||
module_cli_protocol_unix_la_LDFLAGS = -module -avoid-version
|
||||
module_cli_protocol_unix_la_LIBADD = libprotocol-cli.la libiochannel.la
|
||||
module_cli_protocol_unix_la_LIBADD = libprotocol-cli.la libsocket-server.la
|
||||
|
||||
module_native_protocol_tcp_la_SOURCES = module-protocol-stub.c
|
||||
module_native_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_NATIVE $(AM_CFLAGS)
|
||||
module_native_protocol_tcp_la_LDFLAGS = -module -avoid-version
|
||||
module_native_protocol_tcp_la_LIBADD = libprotocol-native.la libiochannel.la libtagstruct.la
|
||||
module_native_protocol_tcp_la_LIBADD = libprotocol-native.la libsocket-server.la
|
||||
|
||||
module_native_protocol_unix_la_SOURCES = module-protocol-stub.c
|
||||
module_native_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_NATIVE $(AM_CFLAGS)
|
||||
module_native_protocol_unix_la_LDFLAGS = -module -avoid-version
|
||||
module_native_protocol_unix_la_LIBADD = libprotocol-native.la libiochannel.la libtagstruct.la
|
||||
module_native_protocol_unix_la_LIBADD = libprotocol-native.la libsocket-server.la
|
||||
|
||||
module_esound_protocol_tcp_la_SOURCES = module-protocol-stub.c
|
||||
module_esound_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_ESOUND $(AM_CFLAGS)
|
||||
module_esound_protocol_tcp_la_LDFLAGS = -module -avoid-version
|
||||
module_esound_protocol_tcp_la_LIBADD = libprotocol-esound.la libsocket-server.la
|
||||
|
||||
module_esound_protocol_unix_la_SOURCES = module-protocol-stub.c
|
||||
module_esound_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_ESOUND $(AM_CFLAGS)
|
||||
module_esound_protocol_unix_la_LDFLAGS = -module -avoid-version
|
||||
module_esound_protocol_unix_la_LIBADD = libprotocol-esound.la libsocket-server.la
|
||||
|
||||
module_pipe_sink_la_SOURCES = module-pipe-sink.c
|
||||
module_pipe_sink_la_LDFLAGS = -module -avoid-version
|
||||
|
|
|
|||
|
|
@ -60,3 +60,9 @@ char *client_list_to_string(struct core *c) {
|
|||
return strbuf_tostring_free(s);
|
||||
}
|
||||
|
||||
|
||||
void client_rename(struct client *c, const char *name) {
|
||||
assert(c);
|
||||
free(c->name);
|
||||
c->name = name ? strdup(name) : NULL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,4 +25,6 @@ void client_kill(struct client *c);
|
|||
|
||||
char *client_list_to_string(struct core *c);
|
||||
|
||||
void client_rename(struct client *c, const char *name);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
191
src/esound-spec.h
Normal file
191
src/esound-spec.h
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
#ifndef fooesoundhfoo
|
||||
#define fooesoundhfoo
|
||||
|
||||
/* Most of the following is blatantly stolen from esound. */
|
||||
|
||||
|
||||
/* path and name of the default EsounD domain socket */
|
||||
#define ESD_UNIX_SOCKET_DIR "/tmp/.esd"
|
||||
#define ESD_UNIX_SOCKET_NAME "/tmp/.esd/socket"
|
||||
|
||||
/* length of the audio buffer size */
|
||||
#define ESD_BUF_SIZE (4 * 1024)
|
||||
/* maximum size we can write(). Otherwise we might overflow */
|
||||
#define ESD_MAX_WRITE_SIZE (21 * 4096)
|
||||
|
||||
/* length of the authorization key, octets */
|
||||
#define ESD_KEY_LEN (16)
|
||||
|
||||
/* default port for the EsounD server */
|
||||
#define ESD_DEFAULT_PORT (16001)
|
||||
|
||||
/* default sample rate for the EsounD server */
|
||||
#define ESD_DEFAULT_RATE (44100)
|
||||
|
||||
/* maximum length of a stream/sample name */
|
||||
#define ESD_NAME_MAX (128)
|
||||
|
||||
/* a magic number to identify the relative endianness of a client */
|
||||
#define ESD_ENDIAN_KEY ((uint32_t) (('E' << 24) + ('N' << 16) + ('D' << 8) + ('N')))
|
||||
|
||||
#define ESD_VOLUME_BASE (256)
|
||||
|
||||
|
||||
/*************************************/
|
||||
/* what can we do to/with the EsounD */
|
||||
enum esd_proto {
|
||||
ESD_PROTO_CONNECT, /* implied on inital client connection */
|
||||
|
||||
/* pseudo "security" functionality */
|
||||
ESD_PROTO_LOCK, /* disable "foreign" client connections */
|
||||
ESD_PROTO_UNLOCK, /* enable "foreign" client connections */
|
||||
|
||||
/* stream functionality: play, record, monitor */
|
||||
ESD_PROTO_STREAM_PLAY, /* play all following data as a stream */
|
||||
ESD_PROTO_STREAM_REC, /* record data from card as a stream */
|
||||
ESD_PROTO_STREAM_MON, /* send mixed buffer output as a stream */
|
||||
|
||||
/* sample functionality: cache, free, play, loop, EOL, kill */
|
||||
ESD_PROTO_SAMPLE_CACHE, /* cache a sample in the server */
|
||||
ESD_PROTO_SAMPLE_FREE, /* release a sample in the server */
|
||||
ESD_PROTO_SAMPLE_PLAY, /* play a cached sample */
|
||||
ESD_PROTO_SAMPLE_LOOP, /* loop a cached sample, til eoloop */
|
||||
ESD_PROTO_SAMPLE_STOP, /* stop a looping sample when done */
|
||||
ESD_PROTO_SAMPLE_KILL, /* stop the looping sample immed. */
|
||||
|
||||
/* free and reclaim /dev/dsp functionality */
|
||||
ESD_PROTO_STANDBY, /* release /dev/dsp and ignore all data */
|
||||
ESD_PROTO_RESUME, /* reclaim /dev/dsp and play sounds again */
|
||||
|
||||
/* TODO: move these to a more logical place. NOTE: will break the protocol */
|
||||
ESD_PROTO_SAMPLE_GETID, /* get the ID for an already-cached sample */
|
||||
ESD_PROTO_STREAM_FILT, /* filter mixed buffer output as a stream */
|
||||
|
||||
/* esd remote management */
|
||||
ESD_PROTO_SERVER_INFO, /* get server info (ver, sample rate, format) */
|
||||
ESD_PROTO_ALL_INFO, /* get all info (server info, players, samples) */
|
||||
ESD_PROTO_SUBSCRIBE, /* track new and removed players and samples */
|
||||
ESD_PROTO_UNSUBSCRIBE, /* stop tracking updates */
|
||||
|
||||
/* esd remote control */
|
||||
ESD_PROTO_STREAM_PAN, /* set stream panning */
|
||||
ESD_PROTO_SAMPLE_PAN, /* set default sample panning */
|
||||
|
||||
/* esd status */
|
||||
ESD_PROTO_STANDBY_MODE, /* see if server is in standby, autostandby, etc */
|
||||
|
||||
/* esd latency */
|
||||
ESD_PROTO_LATENCY, /* retrieve latency between write()'s and output */
|
||||
|
||||
ESD_PROTO_MAX /* for bounds checking */
|
||||
};
|
||||
|
||||
/******************/
|
||||
/* The EsounD api */
|
||||
|
||||
/* the properties of a sound buffer are logically or'd */
|
||||
|
||||
/* bits of stream/sample data */
|
||||
#define ESD_MASK_BITS ( 0x000F )
|
||||
#define ESD_BITS8 ( 0x0000 )
|
||||
#define ESD_BITS16 ( 0x0001 )
|
||||
|
||||
/* how many interleaved channels of data */
|
||||
#define ESD_MASK_CHAN ( 0x00F0 )
|
||||
#define ESD_MONO ( 0x0010 )
|
||||
#define ESD_STEREO ( 0x0020 )
|
||||
|
||||
/* whether it's a stream or a sample */
|
||||
#define ESD_MASK_MODE ( 0x0F00 )
|
||||
#define ESD_STREAM ( 0x0000 )
|
||||
#define ESD_SAMPLE ( 0x0100 )
|
||||
#define ESD_ADPCM ( 0x0200 ) /* TODO: anyone up for this? =P */
|
||||
|
||||
/* the function of the stream/sample, and common functions */
|
||||
#define ESD_MASK_FUNC ( 0xF000 )
|
||||
#define ESD_PLAY ( 0x1000 )
|
||||
/* functions for streams only */
|
||||
#define ESD_MONITOR ( 0x0000 )
|
||||
#define ESD_RECORD ( 0x2000 )
|
||||
/* functions for samples only */
|
||||
#define ESD_STOP ( 0x0000 )
|
||||
#define ESD_LOOP ( 0x2000 )
|
||||
|
||||
typedef int esd_format_t;
|
||||
typedef int esd_proto_t;
|
||||
|
||||
/*******************************************************************/
|
||||
/* esdmgr.c - functions to implement a "sound manager" for esd */
|
||||
|
||||
/* structures to retrieve information about streams/samples from the server */
|
||||
typedef struct esd_server_info {
|
||||
|
||||
int version; /* server version encoded as an int */
|
||||
esd_format_t format; /* magic int with the format info */
|
||||
int rate; /* sample rate */
|
||||
|
||||
} esd_server_info_t;
|
||||
|
||||
typedef struct esd_player_info {
|
||||
|
||||
struct esd_player_info *next; /* point to next entry in list */
|
||||
esd_server_info_t *server; /* the server that contains this stream */
|
||||
|
||||
int source_id; /* either a stream fd or sample id */
|
||||
char name[ ESD_NAME_MAX ]; /* name of stream for remote control */
|
||||
int rate; /* sample rate */
|
||||
int left_vol_scale; /* volume scaling */
|
||||
int right_vol_scale;
|
||||
|
||||
esd_format_t format; /* magic int with the format info */
|
||||
|
||||
} esd_player_info_t;
|
||||
|
||||
typedef struct esd_sample_info {
|
||||
|
||||
struct esd_sample_info *next; /* point to next entry in list */
|
||||
esd_server_info_t *server; /* the server that contains this sample */
|
||||
|
||||
int sample_id; /* either a stream fd or sample id */
|
||||
char name[ ESD_NAME_MAX ]; /* name of stream for remote control */
|
||||
int rate; /* sample rate */
|
||||
int left_vol_scale; /* volume scaling */
|
||||
int right_vol_scale;
|
||||
|
||||
esd_format_t format; /* magic int with the format info */
|
||||
int length; /* total buffer length */
|
||||
|
||||
} esd_sample_info_t;
|
||||
|
||||
typedef struct esd_info {
|
||||
|
||||
esd_server_info_t *server;
|
||||
esd_player_info_t *player_list;
|
||||
esd_sample_info_t *sample_list;
|
||||
|
||||
} esd_info_t;
|
||||
|
||||
enum esd_standby_mode {
|
||||
ESM_ERROR, ESM_ON_STANDBY, ESM_ON_AUTOSTANDBY, ESM_RUNNING
|
||||
};
|
||||
typedef int esd_standby_mode_t;
|
||||
|
||||
enum esd_client_state {
|
||||
ESD_STREAMING_DATA, /* data from here on is streamed data */
|
||||
ESD_CACHING_SAMPLE, /* midway through caching a sample */
|
||||
ESD_NEEDS_REQDATA, /* more data needed to complere request */
|
||||
ESD_NEXT_REQUEST, /* proceed to next request */
|
||||
ESD_CLIENT_STATE_MAX /* place holder */
|
||||
};
|
||||
typedef int esd_client_state_t;
|
||||
|
||||
/* switch endian order for cross platform playing */
|
||||
#define swap_endian_32(x) ((x >> 24) | ((x >> 8) & 0xFF00) | (((x & 0xFF00) << 8)) | (x << 24))
|
||||
|
||||
/* the endian key is transferred in binary, if it's read into int, */
|
||||
/* and matches ESD_ENDIAN_KEY (ENDN), then the endianness of the */
|
||||
/* server and the client match; if it's SWAP_ENDIAN_KEY, swap data */
|
||||
#define ESD_SWAP_ENDIAN_KEY ((uint32_t) swap_endian_32(ESD_ENDIAN_KEY))
|
||||
|
||||
|
||||
#endif
|
||||
|
|
@ -41,11 +41,12 @@ int main(int argc, char *argv[]) {
|
|||
module_load(c, "module-oss-mmap", "/dev/dsp1");
|
||||
/* module_load(c, "module-pipe-sink", NULL);
|
||||
module_load(c, "module-simple-protocol-tcp", NULL);
|
||||
module_load(c, "module-simple-protocol-unix", NULL);*/
|
||||
module_load(c, "module-simple-protocol-unix", NULL);
|
||||
module_load(c, "module-cli-protocol-tcp", NULL);
|
||||
/* module_load(c, "module-cli-protocol-unix", NULL);
|
||||
module_load(c, "module-native-protocol-tcp", NULL);*/
|
||||
module_load(c, "module-native-protocol-unix", NULL);
|
||||
module_load(c, "module-cli-protocol-unix", NULL);
|
||||
module_load(c, "module-native-protocol-tcp", NULL);
|
||||
module_load(c, "module-native-protocol-unix", NULL);*/
|
||||
module_load(c, "module-esound-protocol-tcp", NULL);
|
||||
module_load(c, "module-cli", NULL);
|
||||
|
||||
fprintf(stderr, "main: mainloop entry.\n");
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ int module_init(struct core *c, struct module*m) {
|
|||
char *p;
|
||||
int fd = -1;
|
||||
static const struct pa_sample_spec ss = {
|
||||
.format = SAMPLE_S16NE,
|
||||
.format = PA_SAMPLE_S16NE,
|
||||
.rate = 44100,
|
||||
.channels = 2,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,31 +1,47 @@
|
|||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "module.h"
|
||||
#include "socket-server.h"
|
||||
#include "util.h"
|
||||
|
||||
#ifdef USE_PROTOCOL_SIMPLE
|
||||
#include "protocol-simple.h"
|
||||
#define protocol_free protocol_simple_free
|
||||
#define IPV4_PORT 4711
|
||||
#define UNIX_SOCKET "/tmp/polypaudio_simple"
|
||||
#define UNIX_SOCKET_DIR "/tmp/polypaudio"
|
||||
#define UNIX_SOCKET "/tmp/polypaudio/simple"
|
||||
#else
|
||||
#ifdef USE_PROTOCOL_CLI
|
||||
#include "protocol-cli.h"
|
||||
#define protocol_new protocol_cli_new
|
||||
#define protocol_free protocol_cli_free
|
||||
#define IPV4_PORT 4712
|
||||
#define UNIX_SOCKET "/tmp/polypaudio_cli"
|
||||
#define UNIX_SOCKET_DIR "/tmp/polypaudio"
|
||||
#define UNIX_SOCKET "/tmp/polypaudio/cli"
|
||||
#else
|
||||
#ifdef USE_PROTOCOL_NATIVE
|
||||
#include "protocol-native.h"
|
||||
#define protocol_new protocol_native_new
|
||||
#define protocol_free protocol_native_free
|
||||
#define IPV4_PORT 4713
|
||||
#define UNIX_SOCKET "/tmp/polypaudio_native"
|
||||
#define UNIX_SOCKET_DIR "/tmp/polypaudio"
|
||||
#define UNIX_SOCKET "/tmp/polypaudio/native"
|
||||
#else
|
||||
#error "Broken build system"
|
||||
#endif
|
||||
#ifdef USE_PROTOCOL_ESOUND
|
||||
#include "protocol-esound.h"
|
||||
#include "esound-spec.h"
|
||||
#define protocol_new protocol_esound_new
|
||||
#define protocol_free protocol_esound_free
|
||||
#define IPV4_PORT ESD_DEFAULT_PORT
|
||||
#define UNIX_SOCKET_DIR ESD_UNIX_SOCKET_DIR
|
||||
#define UNIX_SOCKET ESD_UNIX_SOCKET_NAME
|
||||
#else
|
||||
#error "Broken build system"
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
|
@ -37,8 +53,15 @@ int module_init(struct core *c, struct module*m) {
|
|||
if (!(s = socket_server_new_ipv4(c->mainloop, INADDR_LOOPBACK, IPV4_PORT)))
|
||||
return -1;
|
||||
#else
|
||||
if (!(s = socket_server_new_unix(c->mainloop, UNIX_SOCKET)))
|
||||
if (make_secure_dir(UNIX_SOCKET_DIR) < 0) {
|
||||
fprintf(stderr, "Failed to create secure socket directory.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(s = socket_server_new_unix(c->mainloop, UNIX_SOCKET))) {
|
||||
rmdir(UNIX_SOCKET_DIR);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_PROTOCOL_SIMPLE
|
||||
|
|
@ -55,4 +78,8 @@ void module_done(struct core *c, struct module*m) {
|
|||
assert(c && m);
|
||||
|
||||
protocol_free(m->userdata);
|
||||
|
||||
#ifndef USE_TCP_SOCKETS
|
||||
rmdir(UNIX_SOCKET_DIR);
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,11 +22,11 @@ int oss_auto_format(int fd, struct pa_sample_spec *ss) {
|
|||
fprintf(stderr, "SNDCTL_DSP_SETFMT: %s\n", format != AFMT_U8 ? "No supported sample format" : strerror(errno));
|
||||
return -1;
|
||||
} else
|
||||
ss->format = SAMPLE_U8;
|
||||
ss->format = PA_SAMPLE_U8;
|
||||
} else
|
||||
ss->format = f == AFMT_S16_LE ? SAMPLE_S16LE : SAMPLE_S16BE;
|
||||
ss->format = f == AFMT_S16_LE ? PA_SAMPLE_S16LE : PA_SAMPLE_S16BE;
|
||||
} else
|
||||
ss->format = SAMPLE_S16NE;
|
||||
ss->format = PA_SAMPLE_S16NE;
|
||||
|
||||
channels = 2;
|
||||
if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) < 0) {
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ static void stream_complete_callback(struct pa_context*c, struct pa_stream *s, v
|
|||
|
||||
static void context_complete_callback(struct pa_context *c, int success, void *userdata) {
|
||||
static const struct pa_sample_spec ss = {
|
||||
.format = SAMPLE_S16NE,
|
||||
.format = PA_SAMPLE_S16NE,
|
||||
.rate = 44100,
|
||||
.channels = 2
|
||||
};
|
||||
|
|
|
|||
462
src/protocol-esound.c
Normal file
462
src/protocol-esound.c
Normal file
|
|
@ -0,0 +1,462 @@
|
|||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "protocol-esound.h"
|
||||
#include "esound-spec.h"
|
||||
#include "memblock.h"
|
||||
#include "client.h"
|
||||
#include "sinkinput.h"
|
||||
#include "sink.h"
|
||||
#include "sample.h"
|
||||
|
||||
/* This is heavily based on esound's code */
|
||||
|
||||
struct connection {
|
||||
struct protocol_esound *protocol;
|
||||
struct iochannel *io;
|
||||
struct client *client;
|
||||
int authorized, swap_byte_order;
|
||||
void *read_data;
|
||||
size_t read_data_alloc, read_data_length;
|
||||
void *write_data;
|
||||
size_t write_data_alloc, write_data_index, write_data_length;
|
||||
esd_proto_t request;
|
||||
esd_client_state_t state;
|
||||
struct sink_input *sink_input;
|
||||
struct memblockq *input_memblockq;
|
||||
};
|
||||
|
||||
struct protocol_esound {
|
||||
int public;
|
||||
struct core *core;
|
||||
struct socket_server *server;
|
||||
struct idxset *connections;
|
||||
};
|
||||
|
||||
typedef struct proto_handler {
|
||||
size_t data_length;
|
||||
int (*proc)(struct connection *c, const void *data, size_t length);
|
||||
const char *description;
|
||||
} esd_proto_handler_info_t;
|
||||
|
||||
#define BUFSIZE PIPE_BUF
|
||||
|
||||
static void sink_input_drop_cb(struct sink_input *i, size_t length);
|
||||
static int sink_input_peek_cb(struct sink_input *i, struct memchunk *chunk);
|
||||
static void sink_input_kill_cb(struct sink_input *i);
|
||||
static uint32_t sink_input_get_latency_cb(struct sink_input *i);
|
||||
|
||||
static int esd_proto_connect(struct connection *c, const void *data, size_t length);
|
||||
static int esd_proto_stream_play(struct connection *c, const void *data, size_t length);
|
||||
static int esd_proto_stream_record(struct connection *c, const void *data, size_t length);
|
||||
|
||||
static int do_write(struct connection *c);
|
||||
|
||||
/* the big map of protocol handler info */
|
||||
static struct proto_handler proto_map[ESD_PROTO_MAX] = {
|
||||
{ ESD_KEY_LEN + sizeof(int), &esd_proto_connect, "connect" },
|
||||
{ ESD_KEY_LEN + sizeof(int), NULL, "lock" },
|
||||
{ ESD_KEY_LEN + sizeof(int), NULL, "unlock" },
|
||||
|
||||
{ ESD_NAME_MAX + 2 * sizeof(int), &esd_proto_stream_play, "stream play" },
|
||||
{ ESD_NAME_MAX + 2 * sizeof(int), &esd_proto_stream_record, "stream rec" },
|
||||
{ ESD_NAME_MAX + 2 * sizeof(int), NULL, "stream mon" },
|
||||
|
||||
{ ESD_NAME_MAX + 3 * sizeof(int), NULL, "sample cache" },
|
||||
{ sizeof(int), NULL, "sample free" },
|
||||
{ sizeof(int), NULL, "sample play" },
|
||||
{ sizeof(int), NULL, "sample loop" },
|
||||
{ sizeof(int), NULL, "sample stop" },
|
||||
{ -1, NULL, "TODO: sample kill" },
|
||||
|
||||
{ ESD_KEY_LEN + sizeof(int), NULL, "standby" },
|
||||
{ ESD_KEY_LEN + sizeof(int), NULL, "resume" },
|
||||
|
||||
{ ESD_NAME_MAX, NULL, "sample getid" },
|
||||
{ ESD_NAME_MAX + 2 * sizeof(int), NULL, "stream filter" },
|
||||
|
||||
{ sizeof(int), NULL, "server info" },
|
||||
{ sizeof(int), NULL, "all info" },
|
||||
{ -1, NULL, "TODO: subscribe" },
|
||||
{ -1, NULL, "TODO: unsubscribe" },
|
||||
|
||||
{ 3 * sizeof(int), NULL, "stream pan"},
|
||||
{ 3 * sizeof(int), NULL, "sample pan" },
|
||||
|
||||
{ sizeof(int), NULL, "standby mode" },
|
||||
{ 0, NULL, "get latency" }
|
||||
};
|
||||
|
||||
|
||||
static void connection_free(struct connection *c) {
|
||||
assert(c);
|
||||
idxset_remove_by_data(c->protocol->connections, c, NULL);
|
||||
|
||||
client_free(c->client);
|
||||
|
||||
if (c->sink_input)
|
||||
sink_input_free(c->sink_input);
|
||||
if (c->input_memblockq)
|
||||
memblockq_free(c->input_memblockq);
|
||||
|
||||
free(c->read_data);
|
||||
free(c->write_data);
|
||||
|
||||
iochannel_free(c->io);
|
||||
free(c);
|
||||
}
|
||||
|
||||
static struct sink* get_output_sink(struct protocol_esound *p) {
|
||||
assert(p);
|
||||
return sink_get_default(p->core);
|
||||
}
|
||||
|
||||
static void* connection_write(struct connection *c, size_t length) {
|
||||
size_t t, i;
|
||||
assert(c);
|
||||
|
||||
t = c->write_data_length+length;
|
||||
|
||||
if (c->write_data_alloc < t)
|
||||
c->write_data = realloc(c->write_data, c->write_data_alloc = t);
|
||||
|
||||
assert(c->write_data);
|
||||
|
||||
i = c->write_data_length;
|
||||
c->write_data_length += length;
|
||||
|
||||
return c->write_data+i;
|
||||
}
|
||||
|
||||
/*** esound commands ***/
|
||||
|
||||
static int esd_proto_connect(struct connection *c, const void *data, size_t length) {
|
||||
uint32_t ekey;
|
||||
int *ok;
|
||||
assert(length == (ESD_KEY_LEN + sizeof(uint32_t)));
|
||||
|
||||
c->authorized = 1;
|
||||
|
||||
ekey = *(uint32_t*)(data+ESD_KEY_LEN);
|
||||
if (ekey == ESD_ENDIAN_KEY)
|
||||
c->swap_byte_order = 0;
|
||||
else if (ekey == ESD_SWAP_ENDIAN_KEY)
|
||||
c->swap_byte_order = 1;
|
||||
else {
|
||||
fprintf(stderr, "protocol-esound.c: client sent invalid endian key\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ok = connection_write(c, sizeof(int));
|
||||
assert(ok);
|
||||
*ok = 1;
|
||||
|
||||
do_write(c);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int esd_proto_stream_play(struct connection *c, const void *data, size_t length) {
|
||||
char name[ESD_NAME_MAX];
|
||||
int format, rate;
|
||||
struct sink *sink;
|
||||
struct pa_sample_spec ss;
|
||||
assert(length == (sizeof(int)*2+ESD_NAME_MAX));
|
||||
|
||||
format = *(int*)data;
|
||||
rate = *((int*)data + 1);
|
||||
|
||||
if (c->swap_byte_order)
|
||||
format = swap_endian_32(format);
|
||||
if (c->swap_byte_order)
|
||||
rate = swap_endian_32(rate);
|
||||
|
||||
ss.rate = rate;
|
||||
ss.channels = ((format & ESD_MASK_CHAN) == ESD_STEREO) ? 2 : 1;
|
||||
ss.format = ((format & ESD_MASK_BITS) == ESD_BITS16) ? PA_SAMPLE_S16NE : PA_SAMPLE_U8;
|
||||
|
||||
if (!pa_sample_spec_valid(&ss))
|
||||
return -1;
|
||||
|
||||
if (!(sink = get_output_sink(c->protocol)))
|
||||
return -1;
|
||||
|
||||
strncpy(name, data + sizeof(int)*2, sizeof(name));
|
||||
name[sizeof(name)-1] = 0;
|
||||
|
||||
client_rename(c->client, name);
|
||||
|
||||
assert(!c->input_memblockq);
|
||||
c->input_memblockq = memblockq_new(1024*10, pa_sample_size(&ss), 1024*2);
|
||||
assert(c->input_memblockq);
|
||||
|
||||
assert(!c->sink_input);
|
||||
c->sink_input = sink_input_new(sink, &ss, name);
|
||||
assert(c->sink_input);
|
||||
|
||||
c->sink_input->peek = sink_input_peek_cb;
|
||||
c->sink_input->drop = sink_input_drop_cb;
|
||||
c->sink_input->kill = sink_input_kill_cb;
|
||||
c->sink_input->get_latency = sink_input_get_latency_cb;
|
||||
c->sink_input->userdata = c;
|
||||
|
||||
c->state = ESD_STREAMING_DATA;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int esd_proto_stream_record(struct connection *c, const void *data, size_t length) {
|
||||
assert(0);
|
||||
}
|
||||
|
||||
/*** client callbacks ***/
|
||||
|
||||
static void client_kill_cb(struct client *c) {
|
||||
assert(c && c->userdata);
|
||||
connection_free(c->userdata);
|
||||
}
|
||||
|
||||
/*** iochannel callbacks ***/
|
||||
|
||||
static int do_read(struct connection *c) {
|
||||
assert(c && c->io);
|
||||
|
||||
if (!iochannel_is_readable(c->io))
|
||||
return 0;
|
||||
|
||||
if (c->state == ESD_NEXT_REQUEST) {
|
||||
ssize_t r;
|
||||
assert(c->read_data_length < sizeof(c->request));
|
||||
|
||||
if ((r = iochannel_read(c->io, ((void*) &c->request) + c->read_data_length, sizeof(c->request) - c->read_data_length)) <= 0) {
|
||||
fprintf(stderr, "protocol-esound.c: read() failed: %s\n", r == 0 ? "EOF" : strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((c->read_data_length+= r) >= sizeof(c->request)) {
|
||||
struct proto_handler *handler;
|
||||
|
||||
if (c->swap_byte_order)
|
||||
c->request = swap_endian_32(c->request);
|
||||
|
||||
if (c->request < ESD_PROTO_CONNECT || c->request > ESD_PROTO_MAX) {
|
||||
fprintf(stderr, "protocol-esound.c: recieved invalid request.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
handler = proto_map+c->request;
|
||||
|
||||
if (!handler->proc) {
|
||||
fprintf(stderr, "protocol-sound.c: recieved unimplemented request.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (handler->data_length == 0) {
|
||||
c->read_data_length = 0;
|
||||
|
||||
if (handler->proc(c, NULL, 0) < 0)
|
||||
return -1;
|
||||
|
||||
} else {
|
||||
if (c->read_data_alloc < handler->data_length)
|
||||
c->read_data = realloc(c->read_data, c->read_data_alloc = handler->data_length);
|
||||
assert(c->read_data);
|
||||
|
||||
c->state = ESD_NEEDS_REQDATA;
|
||||
c->read_data_length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (c->state == ESD_NEEDS_REQDATA) {
|
||||
ssize_t r;
|
||||
struct proto_handler *handler = proto_map+c->request;
|
||||
|
||||
assert(handler->proc);
|
||||
|
||||
assert(c->read_data && c->read_data_length < handler->data_length);
|
||||
|
||||
if ((r = iochannel_read(c->io, c->read_data + c->read_data_length, handler->data_length - c->read_data_length)) <= 0) {
|
||||
fprintf(stderr, "protocol-esound.c: read() failed: %s\n", r == 0 ? "EOF" : strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((c->read_data_length+= r) >= handler->data_length) {
|
||||
size_t l = c->read_data_length;
|
||||
assert(handler->proc);
|
||||
|
||||
c->state = ESD_NEXT_REQUEST;
|
||||
c->read_data_length = 0;
|
||||
|
||||
if (handler->proc(c, c->read_data, l) < 0)
|
||||
return -1;
|
||||
}
|
||||
} else if (c->state == ESD_STREAMING_DATA) {
|
||||
struct memchunk chunk;
|
||||
ssize_t r;
|
||||
|
||||
assert(c->input_memblockq);
|
||||
|
||||
if (!memblockq_is_writable(c->input_memblockq, BUFSIZE))
|
||||
return 0;
|
||||
|
||||
chunk.memblock = memblock_new(BUFSIZE);
|
||||
assert(chunk.memblock && chunk.memblock->data);
|
||||
|
||||
if ((r = iochannel_read(c->io, chunk.memblock->data, BUFSIZE)) <= 0) {
|
||||
fprintf(stderr, "protocol-esound.c: read() failed: %s\n", r == 0 ? "EOF" : strerror(errno));
|
||||
memblock_unref(chunk.memblock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
chunk.memblock->length = chunk.length = r;
|
||||
chunk.index = 0;
|
||||
|
||||
assert(c->input_memblockq);
|
||||
memblockq_push(c->input_memblockq, &chunk, 0);
|
||||
memblock_unref(chunk.memblock);
|
||||
assert(c->sink_input);
|
||||
sink_notify(c->sink_input->sink);
|
||||
} else
|
||||
assert(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_write(struct connection *c) {
|
||||
ssize_t r;
|
||||
assert(c && c->io);
|
||||
|
||||
if (!iochannel_is_writable(c->io))
|
||||
return 0;
|
||||
|
||||
if (!c->write_data_length)
|
||||
return 0;
|
||||
|
||||
assert(c->write_data_index < c->write_data_length);
|
||||
if ((r = iochannel_write(c->io, c->write_data+c->write_data_index, c->write_data_length-c->write_data_index)) < 0) {
|
||||
fprintf(stderr, "protocol-esound.c: write() failed: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((c->write_data_index +=r) >= c->write_data_length)
|
||||
c->write_data_length = c->write_data_index = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void io_callback(struct iochannel*io, void *userdata) {
|
||||
struct connection *c = userdata;
|
||||
assert(io && c && c->io == io);
|
||||
|
||||
if (do_read(c) < 0 || do_write(c) < 0)
|
||||
connection_free(c);
|
||||
}
|
||||
|
||||
/*** sink_input callbacks ***/
|
||||
|
||||
static int sink_input_peek_cb(struct sink_input *i, struct memchunk *chunk) {
|
||||
struct connection*c;
|
||||
assert(i && i->userdata && chunk);
|
||||
c = i->userdata;
|
||||
|
||||
if (memblockq_peek(c->input_memblockq, chunk) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sink_input_drop_cb(struct sink_input *i, size_t length) {
|
||||
struct connection*c = i->userdata;
|
||||
assert(i && c && length);
|
||||
|
||||
memblockq_drop(c->input_memblockq, length);
|
||||
|
||||
if (do_read(c) < 0)
|
||||
connection_free(c);
|
||||
}
|
||||
|
||||
static void sink_input_kill_cb(struct sink_input *i) {
|
||||
assert(i && i->userdata);
|
||||
connection_free((struct connection *) i->userdata);
|
||||
}
|
||||
|
||||
|
||||
static uint32_t sink_input_get_latency_cb(struct sink_input *i) {
|
||||
struct connection*c = i->userdata;
|
||||
assert(i && c);
|
||||
return pa_samples_usec(memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec);
|
||||
}
|
||||
|
||||
/*** socket server callback ***/
|
||||
|
||||
static void on_connection(struct socket_server*s, struct iochannel *io, void *userdata) {
|
||||
struct connection *c;
|
||||
char cname[256];
|
||||
assert(s && io && userdata);
|
||||
|
||||
c = malloc(sizeof(struct connection));
|
||||
assert(c);
|
||||
c->protocol = userdata;
|
||||
c->io = io;
|
||||
iochannel_set_callback(c->io, io_callback, c);
|
||||
|
||||
iochannel_peer_to_string(io, cname, sizeof(cname));
|
||||
assert(c->protocol->core);
|
||||
c->client = client_new(c->protocol->core, "ESOUND", cname);
|
||||
assert(c->client);
|
||||
c->client->kill = client_kill_cb;
|
||||
c->client->userdata = c;
|
||||
|
||||
c->authorized = c->protocol->public;
|
||||
c->swap_byte_order = 0;
|
||||
|
||||
c->read_data_length = 0;
|
||||
c->read_data = malloc(c->read_data_alloc = proto_map[ESD_PROTO_CONNECT].data_length);
|
||||
assert(c->read_data);
|
||||
|
||||
c->write_data_length = c->write_data_index = c->write_data_alloc = 0;
|
||||
c->write_data = NULL;
|
||||
|
||||
c->state = ESD_NEEDS_REQDATA;
|
||||
c->request = ESD_PROTO_CONNECT;
|
||||
|
||||
c->sink_input = NULL;
|
||||
c->input_memblockq = NULL;
|
||||
|
||||
idxset_put(c->protocol->connections, c, NULL);
|
||||
}
|
||||
|
||||
/*** entry points ***/
|
||||
|
||||
struct protocol_esound* protocol_esound_new(struct core*core, struct socket_server *server) {
|
||||
struct protocol_esound *p;
|
||||
assert(core && server);
|
||||
|
||||
p = malloc(sizeof(struct protocol_esound));
|
||||
assert(p);
|
||||
p->public = 1;
|
||||
p->server = server;
|
||||
p->core = core;
|
||||
p->connections = idxset_new(NULL, NULL);
|
||||
assert(p->connections);
|
||||
|
||||
socket_server_set_callback(p->server, on_connection, p);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
void protocol_esound_free(struct protocol_esound *p) {
|
||||
struct connection *c;
|
||||
assert(p);
|
||||
|
||||
while ((c = idxset_first(p->connections, NULL)))
|
||||
connection_free(c);
|
||||
|
||||
idxset_free(p->connections, NULL, NULL);
|
||||
socket_server_free(p->server);
|
||||
free(p);
|
||||
}
|
||||
12
src/protocol-esound.h
Normal file
12
src/protocol-esound.h
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
#ifndef fooprotocolesoundhfoo
|
||||
#define fooprotocolesoundhfoo
|
||||
|
||||
#include "core.h"
|
||||
#include "socket-server.h"
|
||||
|
||||
struct protocol_esound;
|
||||
|
||||
struct protocol_esound* protocol_esound_new(struct core*core, struct socket_server *server);
|
||||
void protocol_esound_free(struct protocol_esound *p);
|
||||
|
||||
#endif
|
||||
|
|
@ -384,6 +384,7 @@ struct protocol_native* protocol_native_new(struct core *core, struct socket_ser
|
|||
p->server = server;
|
||||
p->core = core;
|
||||
p->connections = idxset_new(NULL, NULL);
|
||||
assert(p->connections);
|
||||
|
||||
socket_server_set_callback(p->server, on_connection, p);
|
||||
|
||||
|
|
|
|||
|
|
@ -73,8 +73,7 @@ static int do_read(struct connection *c) {
|
|||
return -1;
|
||||
}
|
||||
|
||||
chunk.memblock->length = r;
|
||||
chunk.length = r;
|
||||
chunk.memblock->length = chunk.length = r;
|
||||
chunk.index = 0;
|
||||
|
||||
assert(c->input_memblockq);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
#include "sample-util.h"
|
||||
|
||||
struct pa_sample_spec default_sample_spec = {
|
||||
.format = SAMPLE_S16NE,
|
||||
.format = PA_SAMPLE_S16NE,
|
||||
.rate = 44100,
|
||||
.channels = 2
|
||||
};
|
||||
|
|
@ -27,18 +27,20 @@ void silence_memory(void *p, size_t length, struct pa_sample_spec *spec) {
|
|||
assert(p && length && spec);
|
||||
|
||||
switch (spec->format) {
|
||||
case SAMPLE_U8:
|
||||
case PA_SAMPLE_U8:
|
||||
c = 127;
|
||||
break;
|
||||
case SAMPLE_S16LE:
|
||||
case SAMPLE_S16BE:
|
||||
case SAMPLE_FLOAT32:
|
||||
case PA_SAMPLE_S16LE:
|
||||
case PA_SAMPLE_S16BE:
|
||||
case PA_SAMPLE_FLOAT32:
|
||||
c = 0;
|
||||
break;
|
||||
case SAMPLE_ALAW:
|
||||
case SAMPLE_ULAW:
|
||||
case PA_SAMPLE_ALAW:
|
||||
case PA_SAMPLE_ULAW:
|
||||
c = 80;
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
memset(p, c, length);
|
||||
|
|
@ -47,7 +49,7 @@ void silence_memory(void *p, size_t length, struct pa_sample_spec *spec) {
|
|||
size_t mix_chunks(struct mix_info channels[], unsigned nchannels, void *data, size_t length, struct pa_sample_spec *spec, uint8_t volume) {
|
||||
unsigned c, d;
|
||||
assert(channels && data && length && spec);
|
||||
assert(spec->format == SAMPLE_S16NE);
|
||||
assert(spec->format == PA_SAMPLE_S16NE);
|
||||
|
||||
for (d = 0;; d += sizeof(int16_t)) {
|
||||
int32_t sum = 0;
|
||||
|
|
|
|||
26
src/sample.c
26
src/sample.c
|
|
@ -7,18 +7,20 @@ size_t pa_sample_size(struct pa_sample_spec *spec) {
|
|||
size_t b = 1;
|
||||
|
||||
switch (spec->format) {
|
||||
case SAMPLE_U8:
|
||||
case SAMPLE_ULAW:
|
||||
case SAMPLE_ALAW:
|
||||
case PA_SAMPLE_U8:
|
||||
case PA_SAMPLE_ULAW:
|
||||
case PA_SAMPLE_ALAW:
|
||||
b = 1;
|
||||
break;
|
||||
case SAMPLE_S16LE:
|
||||
case SAMPLE_S16BE:
|
||||
case PA_SAMPLE_S16LE:
|
||||
case PA_SAMPLE_S16BE:
|
||||
b = 2;
|
||||
break;
|
||||
case SAMPLE_FLOAT32:
|
||||
case PA_SAMPLE_FLOAT32:
|
||||
b = 4;
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
return b * spec->channels;
|
||||
|
|
@ -35,3 +37,15 @@ uint32_t pa_samples_usec(size_t length, struct pa_sample_spec *spec) {
|
|||
|
||||
return (uint32_t) (((double) length /pa_sample_size(spec))/spec->rate*1000000);
|
||||
}
|
||||
|
||||
int pa_sample_spec_valid(struct pa_sample_spec *spec) {
|
||||
assert(spec);
|
||||
|
||||
if (!spec->rate || !spec->channels)
|
||||
return 0;
|
||||
|
||||
if (spec->format <= 0 || spec->format >= PA_SAMPLE_MAX)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
|
|
|||
17
src/sample.h
17
src/sample.h
|
|
@ -5,15 +5,16 @@
|
|||
#include <sys/types.h>
|
||||
|
||||
enum pa_sample_format {
|
||||
SAMPLE_U8,
|
||||
SAMPLE_ALAW,
|
||||
SAMPLE_ULAW,
|
||||
SAMPLE_S16LE,
|
||||
SAMPLE_S16BE,
|
||||
SAMPLE_FLOAT32
|
||||
PA_SAMPLE_U8,
|
||||
PA_SAMPLE_ALAW,
|
||||
PA_SAMPLE_ULAW,
|
||||
PA_SAMPLE_S16LE,
|
||||
PA_SAMPLE_S16BE,
|
||||
PA_SAMPLE_FLOAT32,
|
||||
PA_SAMPLE_MAX
|
||||
};
|
||||
|
||||
#define SAMPLE_S16NE SAMPLE_S16LE
|
||||
#define PA_SAMPLE_S16NE PA_SAMPLE_S16LE
|
||||
|
||||
struct pa_sample_spec {
|
||||
enum pa_sample_format format;
|
||||
|
|
@ -25,4 +26,6 @@ size_t pa_bytes_per_second(struct pa_sample_spec *spec);
|
|||
size_t pa_sample_size(struct pa_sample_spec *spec);
|
||||
uint32_t pa_samples_usec(size_t length, struct pa_sample_spec *spec);
|
||||
|
||||
int pa_sample_spec_valid(struct pa_sample_spec *spec);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
23
src/util.c
23
src/util.c
|
|
@ -1,9 +1,12 @@
|
|||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/un.h>
|
||||
#include <netinet/in.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
|
|
@ -60,3 +63,23 @@ void peer_to_string(char *c, size_t l, int fd) {
|
|||
|
||||
snprintf(c, l, "Unknown client");
|
||||
}
|
||||
|
||||
int make_secure_dir(const char* dir) {
|
||||
struct stat st;
|
||||
|
||||
if (mkdir(dir, 0700) < 0)
|
||||
if (errno != EEXIST)
|
||||
return -1;
|
||||
|
||||
if (lstat(dir, &st) < 0)
|
||||
goto fail;
|
||||
|
||||
if (!S_ISDIR(st.st_mode) || (st.st_uid != getuid()) || ((st.st_mode & 0777) != 0700))
|
||||
goto fail;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
rmdir(dir);
|
||||
return -1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,4 +5,6 @@ void make_nonblock_fd(int fd);
|
|||
|
||||
void peer_to_string(char *c, size_t l, int fd);
|
||||
|
||||
int make_secure_dir(const char* dir);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue