]> code.delx.au - pulseaudio/blob - src/utils/pacat.c
1) Add flexible seeking support (including absolute) for memory block queues and...
[pulseaudio] / src / utils / pacat.c
1 /* $Id$ */
2
3 /***
4 This file is part of polypaudio.
5
6 polypaudio is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published
8 by the Free Software Foundation; either version 2 of the License,
9 or (at your option) any later version.
10
11 polypaudio is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with polypaudio; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19 USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <signal.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <unistd.h>
30 #include <assert.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <getopt.h>
34
35 #include <polyp/polypaudio.h>
36 #include <polyp/mainloop.h>
37 #include <polyp/mainloop-signal.h>
38
39 #if PA_API_VERSION != 8
40 #error Invalid Polypaudio API version
41 #endif
42
43 static enum { RECORD, PLAYBACK } mode = PLAYBACK;
44
45 static pa_context *context = NULL;
46 static pa_stream *stream = NULL;
47 static pa_mainloop_api *mainloop_api = NULL;
48
49 static void *buffer = NULL;
50 static size_t buffer_length = 0, buffer_index = 0;
51
52 static pa_io_event* stdio_event = NULL;
53
54 static char *stream_name = NULL, *client_name = NULL, *device = NULL;
55
56 static int verbose = 0;
57 static pa_volume_t volume = PA_VOLUME_NORM;
58
59 static pa_sample_spec sample_spec = {
60 .format = PA_SAMPLE_S16LE,
61 .rate = 44100,
62 .channels = 2
63 };
64
65 /* A shortcut for terminating the application */
66 static void quit(int ret) {
67 assert(mainloop_api);
68 mainloop_api->quit(mainloop_api, ret);
69 }
70
71 /* Write some data to the stream */
72 static void do_stream_write(size_t length) {
73 size_t l;
74 assert(length);
75
76 if (!buffer || !buffer_length)
77 return;
78
79 l = length;
80 if (l > buffer_length)
81 l = buffer_length;
82
83 pa_stream_write(stream, (uint8_t*) buffer + buffer_index, l, NULL, 0, PA_SEEK_RELATIVE);
84 buffer_length -= l;
85 buffer_index += l;
86
87 if (!buffer_length) {
88 free(buffer);
89 buffer = NULL;
90 buffer_index = buffer_length = 0;
91 }
92 }
93
94 /* This is called whenever new data may be written to the stream */
95 static void stream_write_callback(pa_stream *s, size_t length, void *userdata) {
96 assert(s && length);
97
98 if (stdio_event)
99 mainloop_api->io_enable(stdio_event, PA_IO_EVENT_INPUT);
100
101 if (!buffer)
102 return;
103
104 do_stream_write(length);
105 }
106
107 /* This is called whenever new data may is available */
108 static void stream_read_callback(pa_stream *s, size_t length, void *userdata) {
109 const void *data;
110 assert(s && length);
111
112 if (stdio_event)
113 mainloop_api->io_enable(stdio_event, PA_IO_EVENT_OUTPUT);
114
115 pa_stream_peek(s, &data, &length);
116 assert(data && length);
117
118 if (buffer) {
119 fprintf(stderr, "Buffer overrun, dropping incoming data\n");
120 pa_stream_drop(s);
121 return;
122 }
123
124 buffer = malloc(buffer_length = length);
125 assert(buffer);
126 memcpy(buffer, data, length);
127 buffer_index = 0;
128 pa_stream_drop(s);
129 }
130
131 /* This routine is called whenever the stream state changes */
132 static void stream_state_callback(pa_stream *s, void *userdata) {
133 assert(s);
134
135 switch (pa_stream_get_state(s)) {
136 case PA_STREAM_CREATING:
137 case PA_STREAM_TERMINATED:
138 break;
139
140 case PA_STREAM_READY:
141 if (verbose)
142 fprintf(stderr, "Stream successfully created\n");
143 break;
144
145 case PA_STREAM_FAILED:
146 default:
147 fprintf(stderr, "Stream error: %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s))));
148 quit(1);
149 }
150 }
151
152 /* This is called whenever the context status changes */
153 static void context_state_callback(pa_context *c, void *userdata) {
154 assert(c);
155
156 switch (pa_context_get_state(c)) {
157 case PA_CONTEXT_CONNECTING:
158 case PA_CONTEXT_AUTHORIZING:
159 case PA_CONTEXT_SETTING_NAME:
160 break;
161
162 case PA_CONTEXT_READY:
163
164 assert(c && !stream);
165
166 if (verbose)
167 fprintf(stderr, "Connection established.\n");
168
169 stream = pa_stream_new(c, stream_name, &sample_spec, NULL);
170 assert(stream);
171
172 pa_stream_set_state_callback(stream, stream_state_callback, NULL);
173 pa_stream_set_write_callback(stream, stream_write_callback, NULL);
174 pa_stream_set_read_callback(stream, stream_read_callback, NULL);
175
176 if (mode == PLAYBACK) {
177 pa_cvolume cv;
178 pa_stream_connect_playback(stream, device, NULL, 0, pa_cvolume_set(&cv, PA_CHANNELS_MAX, volume), NULL);
179 } else
180 pa_stream_connect_record(stream, device, NULL, 0);
181
182 break;
183
184 case PA_CONTEXT_TERMINATED:
185 quit(0);
186 break;
187
188 case PA_CONTEXT_FAILED:
189 default:
190 fprintf(stderr, "Connection failure: %s\n", pa_strerror(pa_context_errno(c)));
191 quit(1);
192 }
193 }
194
195 /* Connection draining complete */
196 static void context_drain_complete(pa_context*c, void *userdata) {
197 pa_context_disconnect(c);
198 }
199
200 /* Stream draining complete */
201 static void stream_drain_complete(pa_stream*s, int success, void *userdata) {
202 pa_operation *o;
203
204 if (!success) {
205 fprintf(stderr, "Failed to drain stream: %s\n", pa_strerror(pa_context_errno(context)));
206 quit(1);
207 }
208
209 if (verbose)
210 fprintf(stderr, "Playback stream drained.\n");
211
212 pa_stream_disconnect(stream);
213 pa_stream_unref(stream);
214 stream = NULL;
215
216 if (!(o = pa_context_drain(context, context_drain_complete, NULL)))
217 pa_context_disconnect(context);
218 else {
219 pa_operation_unref(o);
220
221 if (verbose)
222 fprintf(stderr, "Draining connection to server.\n");
223 }
224 }
225
226 /* New data on STDIN **/
227 static void stdin_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
228 size_t l, w = 0;
229 ssize_t r;
230 assert(a == mainloop_api && e && stdio_event == e);
231
232 if (buffer) {
233 mainloop_api->io_enable(stdio_event, PA_IO_EVENT_NULL);
234 return;
235 }
236
237 if (!stream || pa_stream_get_state(stream) != PA_STREAM_READY || !(l = w = pa_stream_writable_size(stream)))
238 l = 4096;
239
240 buffer = malloc(l);
241 assert(buffer);
242 if ((r = read(fd, buffer, l)) <= 0) {
243 if (r == 0) {
244 if (verbose)
245 fprintf(stderr, "Got EOF.\n");
246 pa_operation_unref(pa_stream_drain(stream, stream_drain_complete, NULL));
247 } else {
248 fprintf(stderr, "read() failed: %s\n", strerror(errno));
249 quit(1);
250 }
251
252 mainloop_api->io_free(stdio_event);
253 stdio_event = NULL;
254 return;
255 }
256
257 buffer_length = r;
258 buffer_index = 0;
259
260 if (w)
261 do_stream_write(w);
262 }
263
264 /* Some data may be written to STDOUT */
265 static void stdout_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
266 ssize_t r;
267 assert(a == mainloop_api && e && stdio_event == e);
268
269 if (!buffer) {
270 mainloop_api->io_enable(stdio_event, PA_IO_EVENT_NULL);
271 return;
272 }
273
274 assert(buffer_length);
275
276 if ((r = write(fd, (uint8_t*) buffer+buffer_index, buffer_length)) <= 0) {
277 fprintf(stderr, "write() failed: %s\n", strerror(errno));
278 quit(1);
279
280 mainloop_api->io_free(stdio_event);
281 stdio_event = NULL;
282 return;
283 }
284
285 buffer_length -= r;
286 buffer_index += r;
287
288 if (!buffer_length) {
289 free(buffer);
290 buffer = NULL;
291 buffer_length = buffer_index = 0;
292 }
293 }
294
295 /* UNIX signal to quit recieved */
296 static void exit_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) {
297 if (verbose)
298 fprintf(stderr, "Got signal, exiting.\n");
299 quit(0);
300
301 }
302
303 /* Show the current latency */
304 static void stream_get_latency_callback(pa_stream *s, const pa_latency_info *i, void *userdata) {
305 pa_usec_t total;
306 int negative = 0;
307 assert(s);
308
309 if (!i) {
310 fprintf(stderr, "Failed to get latency: %s\n", pa_strerror(pa_context_errno(context)));
311 quit(1);
312 return;
313 }
314
315 total = pa_stream_get_latency(s, i, &negative);
316
317 fprintf(stderr, "Latency: buffer: %0.0f usec; sink: %0.0f usec; source: %0.0f usec; transport: %0.0f usec; total: %0.0f usec; synchronized clocks: %s.\n",
318 (float) i->buffer_usec, (float) i->sink_usec, (float) i->source_usec, (float) i->transport_usec, (float) total * (negative?-1:1),
319 i->synchronized_clocks ? "yes" : "no");
320 }
321
322 /* Someone requested that the latency is shown */
323 static void sigusr1_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) {
324 fprintf(stderr, "Got SIGUSR1, requesting latency.\n");
325 pa_operation_unref(pa_stream_get_latency_info(stream, stream_get_latency_callback, NULL));
326 }
327
328
329 static void help(const char *argv0) {
330
331 printf("%s [options]\n\n"
332 " -h, --help Show this help\n"
333 " --version Show version\n\n"
334 " -r, --record Create a connection for recording\n"
335 " -p, --playback Create a connection for playback\n\n"
336 " -v, --verbose Enable verbose operations\n\n"
337 " -s, --server=SERVER The name of the server to connect to\n"
338 " -d, --device=DEVICE The name of the sink/source to connect to\n"
339 " -n, --client-name=NAME How to call this client on the server\n"
340 " --stream-name=NAME How to call this stream on the server\n"
341 " --volume=VOLUME Specify the initial (linear) volume in range 0...256\n"
342 " --rate=SAMPLERATE The sample rate in Hz (defaults to 44100)\n"
343 " --format=SAMPLEFORMAT The sample type, one of s16le, s16be, u8, float32le,\n"
344 " float32be, ulaw, alaw (defaults to s16ne)\n"
345 " --channels=CHANNELS The number of channels, 1 for mono, 2 for stereo\n"
346 " (defaults to 2)\n",
347 argv0);
348 }
349
350 enum {
351 ARG_VERSION = 256,
352 ARG_STREAM_NAME,
353 ARG_VOLUME,
354 ARG_SAMPLERATE,
355 ARG_SAMPLEFORMAT,
356 ARG_CHANNELS
357 };
358
359 int main(int argc, char *argv[]) {
360 pa_mainloop* m = NULL;
361 int ret = 1, r, c;
362 char *bn, *server = NULL;
363
364 static const struct option long_options[] = {
365 {"record", 0, NULL, 'r'},
366 {"playback", 0, NULL, 'p'},
367 {"device", 1, NULL, 'd'},
368 {"server", 1, NULL, 's'},
369 {"client-name", 1, NULL, 'n'},
370 {"stream-name", 1, NULL, ARG_STREAM_NAME},
371 {"version", 0, NULL, ARG_VERSION},
372 {"help", 0, NULL, 'h'},
373 {"verbose", 0, NULL, 'v'},
374 {"volume", 1, NULL, ARG_VOLUME},
375 {"rate", 1, NULL, ARG_SAMPLERATE},
376 {"format", 1, NULL, ARG_SAMPLEFORMAT},
377 {"channels", 1, NULL, ARG_CHANNELS},
378 {NULL, 0, NULL, 0}
379 };
380
381 if (!(bn = strrchr(argv[0], '/')))
382 bn = argv[0];
383 else
384 bn++;
385
386 if (strstr(bn, "rec") || strstr(bn, "mon"))
387 mode = RECORD;
388 else if (strstr(bn, "cat") || strstr(bn, "play"))
389 mode = PLAYBACK;
390
391 while ((c = getopt_long(argc, argv, "rpd:s:n:hv", long_options, NULL)) != -1) {
392
393 switch (c) {
394 case 'h' :
395 help(bn);
396 ret = 0;
397 goto quit;
398
399 case ARG_VERSION:
400 printf("pacat "PACKAGE_VERSION"\nCompiled with libpolyp %s\nLinked with libpolyp %s\n", pa_get_headers_version(), pa_get_library_version());
401 ret = 0;
402 goto quit;
403
404 case 'r':
405 mode = RECORD;
406 break;
407
408 case 'p':
409 mode = PLAYBACK;
410 break;
411
412 case 'd':
413 free(device);
414 device = strdup(optarg);
415 break;
416
417 case 's':
418 free(server);
419 server = strdup(optarg);
420 break;
421
422 case 'n':
423 free(client_name);
424 client_name = strdup(optarg);
425 break;
426
427 case ARG_STREAM_NAME:
428 free(stream_name);
429 stream_name = strdup(optarg);
430 break;
431
432 case 'v':
433 verbose = 1;
434 break;
435
436 case ARG_VOLUME: {
437 int v = atoi(optarg);
438 volume = v < 0 ? 0 : v;
439 break;
440 }
441
442 case ARG_CHANNELS:
443 sample_spec.channels = atoi(optarg);
444 break;
445
446 case ARG_SAMPLEFORMAT:
447 sample_spec.format = pa_parse_sample_format(optarg);
448 break;
449
450 case ARG_SAMPLERATE:
451 sample_spec.rate = atoi(optarg);
452 break;
453
454 default:
455 goto quit;
456 }
457 }
458
459 if (!client_name)
460 client_name = strdup(bn);
461
462 if (!stream_name)
463 stream_name = strdup(client_name);
464
465 if (!pa_sample_spec_valid(&sample_spec)) {
466 fprintf(stderr, "Invalid sample specification\n");
467 goto quit;
468 }
469
470 if (verbose) {
471 char t[PA_SAMPLE_SPEC_SNPRINT_MAX];
472 pa_sample_spec_snprint(t, sizeof(t), &sample_spec);
473 fprintf(stderr, "Opening a %s stream with sample specification '%s'.\n", mode == RECORD ? "recording" : "playback", t);
474 }
475
476 /* Set up a new main loop */
477 if (!(m = pa_mainloop_new())) {
478 fprintf(stderr, "pa_mainloop_new() failed.\n");
479 goto quit;
480 }
481
482 mainloop_api = pa_mainloop_get_api(m);
483
484 r = pa_signal_init(mainloop_api);
485 assert(r == 0);
486 pa_signal_new(SIGINT, exit_signal_callback, NULL);
487 pa_signal_new(SIGTERM, exit_signal_callback, NULL);
488 #ifdef SIGUSR1
489 pa_signal_new(SIGUSR1, sigusr1_signal_callback, NULL);
490 #endif
491 #ifdef SIGPIPE
492 signal(SIGPIPE, SIG_IGN);
493 #endif
494
495 if (!(stdio_event = mainloop_api->io_new(mainloop_api,
496 mode == PLAYBACK ? STDIN_FILENO : STDOUT_FILENO,
497 mode == PLAYBACK ? PA_IO_EVENT_INPUT : PA_IO_EVENT_OUTPUT,
498 mode == PLAYBACK ? stdin_callback : stdout_callback, NULL))) {
499 fprintf(stderr, "source_io() failed.\n");
500 goto quit;
501 }
502
503 /* Create a new connection context */
504 if (!(context = pa_context_new(mainloop_api, client_name))) {
505 fprintf(stderr, "pa_context_new() failed.\n");
506 goto quit;
507 }
508
509 pa_context_set_state_callback(context, context_state_callback, NULL);
510
511 /* Connect the context */
512 pa_context_connect(context, server, 1, NULL);
513
514 /* Run the main loop */
515 if (pa_mainloop_run(m, &ret) < 0) {
516 fprintf(stderr, "pa_mainloop_run() failed.\n");
517 goto quit;
518 }
519
520 quit:
521 if (stream)
522 pa_stream_unref(stream);
523
524 if (context)
525 pa_context_unref(context);
526
527 if (stdio_event) {
528 assert(mainloop_api);
529 mainloop_api->io_free(stdio_event);
530 }
531
532 if (m) {
533 pa_signal_done();
534 pa_mainloop_free(m);
535 }
536
537 free(buffer);
538
539 free(server);
540 free(device);
541 free(client_name);
542 free(stream_name);
543
544 return ret;
545 }