AM_CFLAGS=-ansi -D_GNU_SOURCE
-bin_PROGRAMS = polypaudio pacat pacat-simple
+bin_PROGRAMS = polypaudio pacat pacat-simple parec-simple
pkglib_LTLIBRARIES=libiochannel.la \
libsocket-server.la \
pacat_simple_SOURCES = pacat-simple.c $(libpolyp_la_SOURCES) $(libpolyp_simple_la_SOURCES) $(libpolyp_error_la_SOURCES)
#pacat_simple_LDADD = libpolyp-simple.la libpolyp-error.la
pacat_simple_CFLAGS = $(AM_CFLAGS)
+
+parec_simple_SOURCES = parec-simple.c $(libpolyp_la_SOURCES) $(libpolyp_simple_la_SOURCES) $(libpolyp_error_la_SOURCES)
+#parec_simple_LDADD = libpolyp-simple.la libpolyp-error.la
+parec_simple_CFLAGS = $(AM_CFLAGS)
+#include <stdio.h>
#include <stdlib.h>
#include "polyp-error.h"
[PA_ERROR_PROTOCOL] = "Protocol corrupt",
[PA_ERROR_TIMEOUT] = "Timeout",
[PA_ERROR_AUTHKEY] = "Not authorization key",
- [PA_ERROR_INTERNAL] = "Internal error"
+ [PA_ERROR_INTERNAL] = "Internal error",
+ [PA_ERROR_CONNECTIONTERMINATED] = "Connection terminated",
+ [PA_ERROR_KILLED] = "Entity killed",
};
const char*pa_strerror(uint32_t error) {
struct pa_socket_client *client;
struct pa_pstream *pstream;
struct pa_pdispatch *pdispatch;
- struct pa_dynarray *streams;
+ struct pa_dynarray *record_streams, *playback_streams;
struct pa_stream *first_stream;
uint32_t ctag;
uint32_t error;
};
static void command_request(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
+static void command_playback_stream_killed(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
static const struct pa_pdispatch_command command_table[PA_COMMAND_MAX] = {
[PA_COMMAND_ERROR] = { NULL },
[PA_COMMAND_DELETE_RECORD_STREAM] = { NULL },
[PA_COMMAND_EXIT] = { NULL },
[PA_COMMAND_REQUEST] = { command_request },
+ [PA_COMMAND_PLAYBACK_STREAM_KILLED] = { command_playback_stream_killed },
+ [PA_COMMAND_RECORD_STREAM_KILLED] = { command_playback_stream_killed },
};
struct pa_context *pa_context_new(struct pa_mainloop_api *mainloop, const char *name) {
c->client = NULL;
c->pstream = NULL;
c->pdispatch = NULL;
- c->streams = pa_dynarray_new();
- assert(c->streams);
+ c->playback_streams = pa_dynarray_new();
+ assert(c->playback_streams);
+ c->record_streams = pa_dynarray_new();
+ assert(c->record_streams);
c->first_stream = NULL;
c->error = PA_ERROR_OK;
c->state = CONTEXT_UNCONNECTED;
pa_pdispatch_free(c->pdispatch);
if (c->pstream)
pa_pstream_free(c->pstream);
- if (c->streams)
- pa_dynarray_free(c->streams, NULL, NULL);
+ if (c->record_streams)
+ pa_dynarray_free(c->record_streams, NULL, NULL);
+ if (c->playback_streams)
+ pa_dynarray_free(c->playback_streams, NULL, NULL);
free(c->name);
free(c);
static void pstream_die_callback(struct pa_pstream *p, void *userdata) {
struct pa_context *c = userdata;
assert(p && c);
+ c->error = PA_ERROR_CONNECTIONTERMINATED;
context_dead(c);
}
if (pa_pdispatch_run(c->pdispatch, packet, c) < 0) {
fprintf(stderr, "polyp.c: invalid packet.\n");
+ c->error = PA_ERROR_PROTOCOL;
context_dead(c);
}
}
struct pa_stream *s;
assert(p && chunk && c && chunk->memblock && chunk->memblock->data);
- if (!(s = pa_dynarray_get(c->streams, channel)))
+ if (!(s = pa_dynarray_get(c->record_streams, channel)))
return;
if (s->read_callback)
c->die_userdata = userdata;
}
+static void command_playback_stream_killed(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
+ struct pa_context *c = userdata;
+ struct pa_stream *s;
+ uint32_t channel;
+ assert(pd && (command == PA_COMMAND_PLAYBACK_STREAM_KILLED || command == PA_COMMAND_RECORD_STREAM_KILLED) && t && c);
+
+ if (pa_tagstruct_getu32(t, &channel) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ c->error = PA_ERROR_PROTOCOL;
+ context_dead(c);
+ return;
+ }
+
+ if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_STREAM_KILLED ? c->playback_streams : c->record_streams, channel)))
+ return;
+
+ c->error = PA_ERROR_KILLED;
+ stream_dead(s);
+}
+
static void command_request(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
struct pa_stream *s;
struct pa_context *c = userdata;
return;
}
- if (!(s = pa_dynarray_get(c->streams, channel)))
+ if (!(s = pa_dynarray_get(c->playback_streams, channel)))
return;
if (s->state != STREAM_READY)
}
s->channel_valid = 1;
- pa_dynarray_put(s->context->streams, s->channel, s);
+ pa_dynarray_put((s->direction == PA_STREAM_PLAYBACK) ? s->context->playback_streams : s->context->record_streams, s->channel, s);
s->state = STREAM_READY;
if (s->create_complete_callback)
}
if (s->channel_valid)
- pa_dynarray_put(s->context->streams, s->channel, NULL);
+ pa_dynarray_put((s->direction == PA_STREAM_PLAYBACK) ? s->context->playback_streams : s->context->record_streams, s->channel, NULL);
if (s->next)
s->next->previous = s->previous;
PA_COMMAND_LOOKUP_SINK,
PA_COMMAND_LOOKUP_SOURCE,
PA_COMMAND_DRAIN_PLAYBACK_STREAM,
+ PA_COMMAND_PLAYBACK_STREAM_KILLED,
+ PA_COMMAND_RECORD_STREAM_KILLED,
PA_COMMAND_MAX
};
PA_ERROR_TIMEOUT,
PA_ERROR_AUTHKEY,
PA_ERROR_INTERNAL,
+ PA_ERROR_CONNECTIONTERMINATED,
+ PA_ERROR_KILLED,
PA_ERROR_MAX
};
}
}
+static void send_playback_stream_killed(struct playback_stream *p) {
+ struct pa_tagstruct *t;
+ assert(p);
+
+ t = pa_tagstruct_new(NULL, 0);
+ assert(t);
+ 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);
+}
+
+static void send_record_stream_killed(struct record_stream *r) {
+ struct pa_tagstruct *t;
+ assert(r);
+
+ t = pa_tagstruct_new(NULL, 0);
+ assert(t);
+ 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);
+}
+
+
/*** sinkinput callbacks ***/
static int sink_input_peek_cb(struct pa_sink_input *i, struct pa_memchunk *chunk) {
static void sink_input_kill_cb(struct pa_sink_input *i) {
assert(i && i->userdata);
+ send_playback_stream_killed((struct playback_stream *) i->userdata);
playback_stream_free((struct playback_stream *) i->userdata);
}
static void source_output_kill_cb(struct pa_source_output *o) {
assert(o && o->userdata);
+ send_record_stream_killed((struct record_stream *) o->userdata);
record_stream_free((struct record_stream *) o->userdata);
}
+#include <stdio.h>
+#include <string.h>
#include <assert.h>
#include <stdlib.h>
struct pa_mainloop *mainloop;
struct pa_context *context;
struct pa_stream *stream;
+ enum pa_stream_direction direction;
int dead, drained;
+
+ void *read_data;
+ size_t read_index, read_length;
};
+static void read_callback(struct pa_stream *s, const void*data, size_t length, void *userdata);
+
static int check_error(struct pa_simple *p, int *perror) {
assert(p);
p->mainloop = pa_mainloop_new();
assert(p->mainloop);
p->dead = 0;
+ p->direction = dir;
+ p->read_data = NULL;
+ p->read_index = p->read_length = 0;
if (!(p->context = pa_context_new(pa_mainloop_get_api(p->mainloop), name)))
goto fail;
goto fail;
}
+ pa_stream_set_read_callback(p->stream, read_callback, p);
+
return p;
fail:
void pa_simple_free(struct pa_simple *s) {
assert(s);
+ free(s->read_data);
+
if (s->stream)
pa_stream_free(s->stream);
}
int pa_simple_write(struct pa_simple *p, const void*data, size_t length, int *perror) {
- assert(p && data);
+ assert(p && data && p->direction == PA_STREAM_PLAYBACK);
while (length > 0) {
size_t l;
return 0;
}
-int pa_simple_read(struct pa_simple *s, void*data, size_t length, int *perror) {
- assert(0);
+static void read_callback(struct pa_stream *s, const void*data, size_t length, void *userdata) {
+ struct pa_simple *p = userdata;
+ assert(s && data && length && p);
+
+ if (p->read_data) {
+ fprintf(stderr, __FILE__": Buffer overflow, dropping incoming memory blocks.\n");
+ free(p->read_data);
+ }
+
+ p->read_data = malloc(p->read_length = length);
+ assert(p->read_data);
+ memcpy(p->read_data, data, length);
+ p->read_index = 0;
}
+int pa_simple_read(struct pa_simple *p, void*data, size_t length, int *perror) {
+ assert(p && data && p->direction == PA_STREAM_RECORD);
+
+ while (length > 0) {
+ if (p->read_data) {
+ size_t l = length;
+
+ if (p->read_length <= l)
+ l = p->read_length;
+
+ memcpy(data, p->read_data+p->read_index, l);
+
+ data += l;
+ length -= l;
+
+ p->read_index += l;
+ p->read_length -= l;
+
+ if (!p->read_length) {
+ free(p->read_data);
+ p->read_data = NULL;
+ p->read_index = 0;
+ }
+
+ if (!length)
+ return 0;
+
+ assert(!p->read_data);
+ }
+
+ if (iterate(p, 1, perror) < 0)
+ return -1;
+ }
+
+ return 0;
+}
static void drain_complete(struct pa_stream *s, void *userdata) {
struct pa_simple *p = userdata;
}
int pa_simple_drain(struct pa_simple *p, int *perror) {
- assert(p);
+ assert(p && p->direction == PA_STREAM_PLAYBACK);
p->drained = 0;
pa_stream_drain(p->stream, drain_complete, p);
-- implement parec-simple
- native library/protocol:
more functions (esp. latency)
- svn-id and license in every file
- documentation
-
+- dependency checking script
-- post 0.1
- future cancellation