]> code.delx.au - pulseaudio/blob - src/utils/pacat.c
Merge commit 'origin/master-tx'
[pulseaudio] / src / utils / pacat.c
1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2004-2006 Lennart Poettering
5 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
6
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published
9 by the Free Software Foundation; either version 2.1 of the License,
10 or (at your option) any later version.
11
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with PulseAudio; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 USA.
21 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <signal.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <unistd.h>
31 #include <assert.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <getopt.h>
35 #include <fcntl.h>
36 #include <locale.h>
37
38 #include <pulse/i18n.h>
39 #include <pulse/pulseaudio.h>
40
41 #define TIME_EVENT_USEC 50000
42
43 #define CLEAR_LINE "\x1B[K"
44
45 static enum { RECORD, PLAYBACK } mode = PLAYBACK;
46
47 static pa_context *context = NULL;
48 static pa_stream *stream = NULL;
49 static pa_mainloop_api *mainloop_api = NULL;
50
51 static void *buffer = NULL;
52 static size_t buffer_length = 0, buffer_index = 0;
53
54 static pa_io_event* stdio_event = NULL;
55
56 static char *stream_name = NULL, *client_name = NULL, *device = NULL;
57
58 static int verbose = 0;
59 static pa_volume_t volume = PA_VOLUME_NORM;
60 static int volume_is_set = 0;
61
62 static pa_sample_spec sample_spec = {
63 .format = PA_SAMPLE_S16LE,
64 .rate = 44100,
65 .channels = 2
66 };
67
68 static pa_channel_map channel_map;
69 static int channel_map_set = 0;
70
71 static pa_stream_flags_t flags = 0;
72
73 static size_t latency = 0, process_time=0;
74
75 /* A shortcut for terminating the application */
76 static void quit(int ret) {
77 assert(mainloop_api);
78 mainloop_api->quit(mainloop_api, ret);
79 }
80
81 /* Write some data to the stream */
82 static void do_stream_write(size_t length) {
83 size_t l;
84 assert(length);
85
86 if (!buffer || !buffer_length)
87 return;
88
89 l = length;
90 if (l > buffer_length)
91 l = buffer_length;
92
93 if (pa_stream_write(stream, (uint8_t*) buffer + buffer_index, l, NULL, 0, PA_SEEK_RELATIVE) < 0) {
94 fprintf(stderr, _("pa_stream_write() failed: %s\n"), pa_strerror(pa_context_errno(context)));
95 quit(1);
96 return;
97 }
98
99 buffer_length -= l;
100 buffer_index += l;
101
102 if (!buffer_length) {
103 pa_xfree(buffer);
104 buffer = NULL;
105 buffer_index = buffer_length = 0;
106 }
107 }
108
109 /* This is called whenever new data may be written to the stream */
110 static void stream_write_callback(pa_stream *s, size_t length, void *userdata) {
111 assert(s);
112 assert(length > 0);
113
114 if (stdio_event)
115 mainloop_api->io_enable(stdio_event, PA_IO_EVENT_INPUT);
116
117 if (!buffer)
118 return;
119
120 do_stream_write(length);
121 }
122
123 /* This is called whenever new data may is available */
124 static void stream_read_callback(pa_stream *s, size_t length, void *userdata) {
125 const void *data;
126 assert(s);
127 assert(length > 0);
128
129 if (stdio_event)
130 mainloop_api->io_enable(stdio_event, PA_IO_EVENT_OUTPUT);
131
132 if (pa_stream_peek(s, &data, &length) < 0) {
133 fprintf(stderr, _("pa_stream_peek() failed: %s\n"), pa_strerror(pa_context_errno(context)));
134 quit(1);
135 return;
136 }
137
138 assert(data);
139 assert(length > 0);
140
141 if (buffer) {
142 buffer = pa_xrealloc(buffer, buffer_length + length);
143 memcpy((uint8_t*) buffer + buffer_length, data, length);
144 buffer_length += length;
145 } else {
146 buffer = pa_xmalloc(length);
147 memcpy(buffer, data, length);
148 buffer_length = length;
149 buffer_index = 0;
150 }
151
152 pa_stream_drop(s);
153 }
154
155 /* This routine is called whenever the stream state changes */
156 static void stream_state_callback(pa_stream *s, void *userdata) {
157 assert(s);
158
159 switch (pa_stream_get_state(s)) {
160 case PA_STREAM_CREATING:
161 case PA_STREAM_TERMINATED:
162 break;
163
164 case PA_STREAM_READY:
165 if (verbose) {
166 const pa_buffer_attr *a;
167 char cmt[PA_CHANNEL_MAP_SNPRINT_MAX], sst[PA_SAMPLE_SPEC_SNPRINT_MAX];
168
169 fprintf(stderr, _("Stream successfully created.\n"));
170
171 if (!(a = pa_stream_get_buffer_attr(s)))
172 fprintf(stderr, _("pa_stream_get_buffer_attr() failed: %s\n"), pa_strerror(pa_context_errno(pa_stream_get_context(s))));
173 else {
174
175 if (mode == PLAYBACK)
176 fprintf(stderr, _("Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u\n"), a->maxlength, a->tlength, a->prebuf, a->minreq);
177 else {
178 assert(mode == RECORD);
179 fprintf(stderr, _("Buffer metrics: maxlength=%u, fragsize=%u\n"), a->maxlength, a->fragsize);
180 }
181 }
182
183 fprintf(stderr, _("Using sample spec '%s', channel map '%s'.\n"),
184 pa_sample_spec_snprint(sst, sizeof(sst), pa_stream_get_sample_spec(s)),
185 pa_channel_map_snprint(cmt, sizeof(cmt), pa_stream_get_channel_map(s)));
186
187 fprintf(stderr, _("Connected to device %s (%u, %ssuspended).\n"),
188 pa_stream_get_device_name(s),
189 pa_stream_get_device_index(s),
190 pa_stream_is_suspended(s) ? "" : "not ");
191 }
192
193 break;
194
195 case PA_STREAM_FAILED:
196 default:
197 fprintf(stderr, _("Stream error: %s\n"), pa_strerror(pa_context_errno(pa_stream_get_context(s))));
198 quit(1);
199 }
200 }
201
202 static void stream_suspended_callback(pa_stream *s, void *userdata) {
203 assert(s);
204
205 if (verbose) {
206 if (pa_stream_is_suspended(s))
207 fprintf(stderr, _("Stream device suspended.%s \n"), CLEAR_LINE);
208 else
209 fprintf(stderr, _("Stream device resumed.%s \n"), CLEAR_LINE);
210 }
211 }
212
213 static void stream_underflow_callback(pa_stream *s, void *userdata) {
214 assert(s);
215
216 if (verbose)
217 fprintf(stderr, _("Stream underrun.%s \n"), CLEAR_LINE);
218 }
219
220 static void stream_overflow_callback(pa_stream *s, void *userdata) {
221 assert(s);
222
223 if (verbose)
224 fprintf(stderr, _("Stream overrun.%s \n"), CLEAR_LINE);
225 }
226
227 static void stream_started_callback(pa_stream *s, void *userdata) {
228 assert(s);
229
230 if (verbose)
231 fprintf(stderr, _("Stream started.%s \n"), CLEAR_LINE);
232 }
233
234 static void stream_moved_callback(pa_stream *s, void *userdata) {
235 assert(s);
236
237 if (verbose)
238 fprintf(stderr, _("Stream moved to device %s (%u, %ssuspended).%s \n"), pa_stream_get_device_name(s), pa_stream_get_device_index(s), pa_stream_is_suspended(s) ? "" : _("not "), CLEAR_LINE);
239 }
240
241 static void stream_event_callback(pa_stream *s, const char *name, pa_proplist *pl, void *userdata) {
242 char *t;
243
244 assert(s);
245 assert(name);
246 assert(pl);
247
248 t = pa_proplist_to_string_sep(pl, ", ");
249 fprintf(stderr, "Got event '%s', properties '%s'\n", name, t);
250 pa_xfree(t);
251 }
252
253 /* This is called whenever the context status changes */
254 static void context_state_callback(pa_context *c, void *userdata) {
255 assert(c);
256
257 switch (pa_context_get_state(c)) {
258 case PA_CONTEXT_CONNECTING:
259 case PA_CONTEXT_AUTHORIZING:
260 case PA_CONTEXT_SETTING_NAME:
261 break;
262
263 case PA_CONTEXT_READY: {
264 int r;
265 pa_buffer_attr buffer_attr;
266
267 assert(c);
268 assert(!stream);
269
270 if (verbose)
271 fprintf(stderr, _("Connection established.%s \n"), CLEAR_LINE);
272
273 if (!(stream = pa_stream_new(c, stream_name, &sample_spec, channel_map_set ? &channel_map : NULL))) {
274 fprintf(stderr, _("pa_stream_new() failed: %s\n"), pa_strerror(pa_context_errno(c)));
275 goto fail;
276 }
277
278 pa_stream_set_state_callback(stream, stream_state_callback, NULL);
279 pa_stream_set_write_callback(stream, stream_write_callback, NULL);
280 pa_stream_set_read_callback(stream, stream_read_callback, NULL);
281 pa_stream_set_suspended_callback(stream, stream_suspended_callback, NULL);
282 pa_stream_set_moved_callback(stream, stream_moved_callback, NULL);
283 pa_stream_set_underflow_callback(stream, stream_underflow_callback, NULL);
284 pa_stream_set_overflow_callback(stream, stream_overflow_callback, NULL);
285 pa_stream_set_started_callback(stream, stream_started_callback, NULL);
286 pa_stream_set_event_callback(stream, stream_event_callback, NULL);
287
288 if (latency > 0) {
289 memset(&buffer_attr, 0, sizeof(buffer_attr));
290 buffer_attr.tlength = (uint32_t) latency;
291 buffer_attr.minreq = (uint32_t) process_time;
292 buffer_attr.maxlength = (uint32_t) -1;
293 buffer_attr.prebuf = (uint32_t) -1;
294 buffer_attr.fragsize = (uint32_t) latency;
295 flags |= PA_STREAM_ADJUST_LATENCY;
296 }
297
298 if (mode == PLAYBACK) {
299 pa_cvolume cv;
300 if ((r = pa_stream_connect_playback(stream, device, latency > 0 ? &buffer_attr : NULL, flags, volume_is_set ? pa_cvolume_set(&cv, sample_spec.channels, volume) : NULL, NULL)) < 0) {
301 fprintf(stderr, _("pa_stream_connect_playback() failed: %s\n"), pa_strerror(pa_context_errno(c)));
302 goto fail;
303 }
304
305 } else {
306 if ((r = pa_stream_connect_record(stream, device, latency > 0 ? &buffer_attr : NULL, flags)) < 0) {
307 fprintf(stderr, _("pa_stream_connect_record() failed: %s\n"), pa_strerror(pa_context_errno(c)));
308 goto fail;
309 }
310 }
311
312 break;
313 }
314
315 case PA_CONTEXT_TERMINATED:
316 quit(0);
317 break;
318
319 case PA_CONTEXT_FAILED:
320 default:
321 fprintf(stderr, _("Connection failure: %s\n"), pa_strerror(pa_context_errno(c)));
322 goto fail;
323 }
324
325 return;
326
327 fail:
328 quit(1);
329
330 }
331
332 /* Connection draining complete */
333 static void context_drain_complete(pa_context*c, void *userdata) {
334 pa_context_disconnect(c);
335 }
336
337 /* Stream draining complete */
338 static void stream_drain_complete(pa_stream*s, int success, void *userdata) {
339
340 if (!success) {
341 fprintf(stderr, _("Failed to drain stream: %s\n"), pa_strerror(pa_context_errno(context)));
342 quit(1);
343 }
344
345 if (verbose)
346 fprintf(stderr, _("Playback stream drained.\n"));
347
348 pa_stream_disconnect(stream);
349 pa_stream_unref(stream);
350 stream = NULL;
351
352 if (!pa_context_drain(context, context_drain_complete, NULL))
353 pa_context_disconnect(context);
354 else {
355 if (verbose)
356 fprintf(stderr, _("Draining connection to server.\n"));
357 }
358 }
359
360 /* New data on STDIN **/
361 static void stdin_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
362 size_t l, w = 0;
363 ssize_t r;
364
365 assert(a == mainloop_api);
366 assert(e);
367 assert(stdio_event == e);
368
369 if (buffer) {
370 mainloop_api->io_enable(stdio_event, PA_IO_EVENT_NULL);
371 return;
372 }
373
374 if (!stream || pa_stream_get_state(stream) != PA_STREAM_READY || !(l = w = pa_stream_writable_size(stream)))
375 l = 4096;
376
377 buffer = pa_xmalloc(l);
378
379 if ((r = read(fd, buffer, l)) <= 0) {
380 if (r == 0) {
381 if (verbose)
382 fprintf(stderr, _("Got EOF.\n"));
383
384 if (stream) {
385 pa_operation *o;
386
387 if (!(o = pa_stream_drain(stream, stream_drain_complete, NULL))) {
388 fprintf(stderr, _("pa_stream_drain(): %s\n"), pa_strerror(pa_context_errno(context)));
389 quit(1);
390 return;
391 }
392
393 pa_operation_unref(o);
394 } else
395 quit(0);
396
397 } else {
398 fprintf(stderr, _("read() failed: %s\n"), strerror(errno));
399 quit(1);
400 }
401
402 mainloop_api->io_free(stdio_event);
403 stdio_event = NULL;
404 return;
405 }
406
407 buffer_length = (uint32_t) r;
408 buffer_index = 0;
409
410 if (w)
411 do_stream_write(w);
412 }
413
414 /* Some data may be written to STDOUT */
415 static void stdout_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
416 ssize_t r;
417
418 assert(a == mainloop_api);
419 assert(e);
420 assert(stdio_event == e);
421
422 if (!buffer) {
423 mainloop_api->io_enable(stdio_event, PA_IO_EVENT_NULL);
424 return;
425 }
426
427 assert(buffer_length);
428
429 if ((r = write(fd, (uint8_t*) buffer+buffer_index, buffer_length)) <= 0) {
430 fprintf(stderr, _("write() failed: %s\n"), strerror(errno));
431 quit(1);
432
433 mainloop_api->io_free(stdio_event);
434 stdio_event = NULL;
435 return;
436 }
437
438 buffer_length -= (uint32_t) r;
439 buffer_index += (uint32_t) r;
440
441 if (!buffer_length) {
442 pa_xfree(buffer);
443 buffer = NULL;
444 buffer_length = buffer_index = 0;
445 }
446 }
447
448 /* UNIX signal to quit recieved */
449 static void exit_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) {
450 if (verbose)
451 fprintf(stderr, _("Got signal, exiting.\n"));
452 quit(0);
453 }
454
455 /* Show the current latency */
456 static void stream_update_timing_callback(pa_stream *s, int success, void *userdata) {
457 pa_usec_t l, usec;
458 int negative = 0;
459
460 assert(s);
461
462 if (!success ||
463 pa_stream_get_time(s, &usec) < 0 ||
464 pa_stream_get_latency(s, &l, &negative) < 0) {
465 fprintf(stderr, _("Failed to get latency: %s\n"), pa_strerror(pa_context_errno(context)));
466 quit(1);
467 return;
468 }
469
470 fprintf(stderr, _("Time: %0.3f sec; Latency: %0.0f usec. \r"),
471 (float) usec / 1000000,
472 (float) l * (negative?-1.0f:1.0f));
473 }
474
475 /* Someone requested that the latency is shown */
476 static void sigusr1_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) {
477
478 if (!stream)
479 return;
480
481 pa_operation_unref(pa_stream_update_timing_info(stream, stream_update_timing_callback, NULL));
482 }
483
484 static void time_event_callback(pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata) {
485 struct timeval next;
486
487 if (stream && pa_stream_get_state(stream) == PA_STREAM_READY) {
488 pa_operation *o;
489 if (!(o = pa_stream_update_timing_info(stream, stream_update_timing_callback, NULL)))
490 fprintf(stderr, _("pa_stream_update_timing_info() failed: %s\n"), pa_strerror(pa_context_errno(context)));
491 else
492 pa_operation_unref(o);
493 }
494
495 pa_gettimeofday(&next);
496 pa_timeval_add(&next, TIME_EVENT_USEC);
497
498 m->time_restart(e, &next);
499 }
500
501 static void help(const char *argv0) {
502
503 printf(_("%s [options]\n\n"
504 " -h, --help Show this help\n"
505 " --version Show version\n\n"
506 " -r, --record Create a connection for recording\n"
507 " -p, --playback Create a connection for playback\n\n"
508 " -v, --verbose Enable verbose operations\n\n"
509 " -s, --server=SERVER The name of the server to connect to\n"
510 " -d, --device=DEVICE The name of the sink/source to connect to\n"
511 " -n, --client-name=NAME How to call this client on the server\n"
512 " --stream-name=NAME How to call this stream on the server\n"
513 " --volume=VOLUME Specify the initial (linear) volume in range 0...65536\n"
514 " --rate=SAMPLERATE The sample rate in Hz (defaults to 44100)\n"
515 " --format=SAMPLEFORMAT The sample type, one of s16le, s16be, u8, float32le,\n"
516 " float32be, ulaw, alaw, s32le, s32be (defaults to s16ne)\n"
517 " --channels=CHANNELS The number of channels, 1 for mono, 2 for stereo\n"
518 " (defaults to 2)\n"
519 " --channel-map=CHANNELMAP Channel map to use instead of the default\n"
520 " --fix-format Take the sample format from the sink the stream is\n"
521 " being connected to.\n"
522 " --fix-rate Take the sampling rate from the sink the stream is\n"
523 " being connected to.\n"
524 " --fix-channels Take the number of channels and the channel map\n"
525 " from the sink the stream is being connected to.\n"
526 " --no-remix Don't upmix or downmix channels.\n"
527 " --no-remap Map channels by index instead of name.\n"
528 " --latency=BYTES Request the specified latency in bytes.\n"
529 " --process-time=BYTES Request the specified process time per request in bytes.\n")
530 ,
531 argv0);
532 }
533
534 enum {
535 ARG_VERSION = 256,
536 ARG_STREAM_NAME,
537 ARG_VOLUME,
538 ARG_SAMPLERATE,
539 ARG_SAMPLEFORMAT,
540 ARG_CHANNELS,
541 ARG_CHANNELMAP,
542 ARG_FIX_FORMAT,
543 ARG_FIX_RATE,
544 ARG_FIX_CHANNELS,
545 ARG_NO_REMAP,
546 ARG_NO_REMIX,
547 ARG_LATENCY,
548 ARG_PROCESS_TIME
549 };
550
551 int main(int argc, char *argv[]) {
552 pa_mainloop* m = NULL;
553 int ret = 1, r, c;
554 char *bn, *server = NULL;
555 pa_time_event *time_event = NULL;
556
557 static const struct option long_options[] = {
558 {"record", 0, NULL, 'r'},
559 {"playback", 0, NULL, 'p'},
560 {"device", 1, NULL, 'd'},
561 {"server", 1, NULL, 's'},
562 {"client-name", 1, NULL, 'n'},
563 {"stream-name", 1, NULL, ARG_STREAM_NAME},
564 {"version", 0, NULL, ARG_VERSION},
565 {"help", 0, NULL, 'h'},
566 {"verbose", 0, NULL, 'v'},
567 {"volume", 1, NULL, ARG_VOLUME},
568 {"rate", 1, NULL, ARG_SAMPLERATE},
569 {"format", 1, NULL, ARG_SAMPLEFORMAT},
570 {"channels", 1, NULL, ARG_CHANNELS},
571 {"channel-map", 1, NULL, ARG_CHANNELMAP},
572 {"fix-format", 0, NULL, ARG_FIX_FORMAT},
573 {"fix-rate", 0, NULL, ARG_FIX_RATE},
574 {"fix-channels", 0, NULL, ARG_FIX_CHANNELS},
575 {"no-remap", 0, NULL, ARG_NO_REMAP},
576 {"no-remix", 0, NULL, ARG_NO_REMIX},
577 {"latency", 1, NULL, ARG_LATENCY},
578 {"process-time", 1, NULL, ARG_PROCESS_TIME},
579 {NULL, 0, NULL, 0}
580 };
581
582 setlocale(LC_ALL, "");
583 bindtextdomain(GETTEXT_PACKAGE, PULSE_LOCALEDIR);
584
585 if (!(bn = strrchr(argv[0], '/')))
586 bn = argv[0];
587 else
588 bn++;
589
590 if (strstr(bn, "rec") || strstr(bn, "mon"))
591 mode = RECORD;
592 else if (strstr(bn, "cat") || strstr(bn, "play"))
593 mode = PLAYBACK;
594
595 while ((c = getopt_long(argc, argv, "rpd:s:n:hv", long_options, NULL)) != -1) {
596
597 switch (c) {
598 case 'h' :
599 help(bn);
600 ret = 0;
601 goto quit;
602
603 case ARG_VERSION:
604 printf(_("pacat %s\nCompiled with libpulse %s\nLinked with libpulse %s\n"), PACKAGE_VERSION, pa_get_headers_version(), pa_get_library_version());
605 ret = 0;
606 goto quit;
607
608 case 'r':
609 mode = RECORD;
610 break;
611
612 case 'p':
613 mode = PLAYBACK;
614 break;
615
616 case 'd':
617 pa_xfree(device);
618 device = pa_xstrdup(optarg);
619 break;
620
621 case 's':
622 pa_xfree(server);
623 server = pa_xstrdup(optarg);
624 break;
625
626 case 'n':
627 pa_xfree(client_name);
628 client_name = pa_xstrdup(optarg);
629 break;
630
631 case ARG_STREAM_NAME:
632 pa_xfree(stream_name);
633 stream_name = pa_xstrdup(optarg);
634 break;
635
636 case 'v':
637 verbose = 1;
638 break;
639
640 case ARG_VOLUME: {
641 int v = atoi(optarg);
642 volume = v < 0 ? 0U : (pa_volume_t) v;
643 volume_is_set = 1;
644 break;
645 }
646
647 case ARG_CHANNELS:
648 sample_spec.channels = (uint8_t) atoi(optarg);
649 break;
650
651 case ARG_SAMPLEFORMAT:
652 sample_spec.format = pa_parse_sample_format(optarg);
653 break;
654
655 case ARG_SAMPLERATE:
656 sample_spec.rate = (uint32_t) atoi(optarg);
657 break;
658
659 case ARG_CHANNELMAP:
660 if (!pa_channel_map_parse(&channel_map, optarg)) {
661 fprintf(stderr, _("Invalid channel map '%s'\n"), optarg);
662 goto quit;
663 }
664
665 channel_map_set = 1;
666 break;
667
668 case ARG_FIX_CHANNELS:
669 flags |= PA_STREAM_FIX_CHANNELS;
670 break;
671
672 case ARG_FIX_RATE:
673 flags |= PA_STREAM_FIX_RATE;
674 break;
675
676 case ARG_FIX_FORMAT:
677 flags |= PA_STREAM_FIX_FORMAT;
678 break;
679
680 case ARG_NO_REMIX:
681 flags |= PA_STREAM_NO_REMIX_CHANNELS;
682 break;
683
684 case ARG_NO_REMAP:
685 flags |= PA_STREAM_NO_REMAP_CHANNELS;
686 break;
687
688 case ARG_LATENCY:
689 if (((latency = (size_t) atoi(optarg))) <= 0) {
690 fprintf(stderr, _("Invalid latency specification '%s'\n"), optarg);
691 goto quit;
692 }
693 break;
694
695 case ARG_PROCESS_TIME:
696 if (((process_time = (size_t) atoi(optarg))) <= 0) {
697 fprintf(stderr, _("Invalid process time specification '%s'\n"), optarg);
698 goto quit;
699 }
700 break;
701
702 default:
703 goto quit;
704 }
705 }
706
707 if (!pa_sample_spec_valid(&sample_spec)) {
708 fprintf(stderr, _("Invalid sample specification\n"));
709 goto quit;
710 }
711
712 if (channel_map_set && pa_channel_map_compatible(&channel_map, &sample_spec)) {
713 fprintf(stderr, _("Channel map doesn't match sample specification\n"));
714 goto quit;
715 }
716
717 if (verbose) {
718 char t[PA_SAMPLE_SPEC_SNPRINT_MAX];
719 pa_sample_spec_snprint(t, sizeof(t), &sample_spec);
720 fprintf(stderr, _("Opening a %s stream with sample specification '%s'.\n"), mode == RECORD ? _("recording") : _("playback"), t);
721 }
722
723 if (!(optind >= argc)) {
724 if (optind+1 == argc) {
725 int fd;
726
727 if ((fd = open(argv[optind], mode == PLAYBACK ? O_RDONLY : O_WRONLY|O_TRUNC|O_CREAT, 0666)) < 0) {
728 fprintf(stderr, _("open(): %s\n"), strerror(errno));
729 goto quit;
730 }
731
732 if (dup2(fd, mode == PLAYBACK ? 0 : 1) < 0) {
733 fprintf(stderr, _("dup2(): %s\n"), strerror(errno));
734 goto quit;
735 }
736
737 close(fd);
738
739 if (!stream_name)
740 stream_name = pa_xstrdup(argv[optind]);
741
742 } else {
743 fprintf(stderr, _("Too many arguments.\n"));
744 goto quit;
745 }
746 }
747
748 if (!client_name)
749 client_name = pa_xstrdup(bn);
750
751 if (!stream_name)
752 stream_name = pa_xstrdup(client_name);
753
754 /* Set up a new main loop */
755 if (!(m = pa_mainloop_new())) {
756 fprintf(stderr, _("pa_mainloop_new() failed.\n"));
757 goto quit;
758 }
759
760 mainloop_api = pa_mainloop_get_api(m);
761
762 r = pa_signal_init(mainloop_api);
763 assert(r == 0);
764 pa_signal_new(SIGINT, exit_signal_callback, NULL);
765 pa_signal_new(SIGTERM, exit_signal_callback, NULL);
766 #ifdef SIGUSR1
767 pa_signal_new(SIGUSR1, sigusr1_signal_callback, NULL);
768 #endif
769 #ifdef SIGPIPE
770 signal(SIGPIPE, SIG_IGN);
771 #endif
772
773 if (!(stdio_event = mainloop_api->io_new(mainloop_api,
774 mode == PLAYBACK ? STDIN_FILENO : STDOUT_FILENO,
775 mode == PLAYBACK ? PA_IO_EVENT_INPUT : PA_IO_EVENT_OUTPUT,
776 mode == PLAYBACK ? stdin_callback : stdout_callback, NULL))) {
777 fprintf(stderr, _("io_new() failed.\n"));
778 goto quit;
779 }
780
781 /* Create a new connection context */
782 if (!(context = pa_context_new(mainloop_api, client_name))) {
783 fprintf(stderr, _("pa_context_new() failed.\n"));
784 goto quit;
785 }
786
787 pa_context_set_state_callback(context, context_state_callback, NULL);
788
789 /* Connect the context */
790 if (pa_context_connect(context, server, 0, NULL) < 0) {
791 fprintf(stderr, _("pa_context_connect() failed: %s"), pa_strerror(pa_context_errno(context)));
792 goto quit;
793 }
794
795 if (verbose) {
796 struct timeval tv;
797
798 pa_gettimeofday(&tv);
799 pa_timeval_add(&tv, TIME_EVENT_USEC);
800
801 if (!(time_event = mainloop_api->time_new(mainloop_api, &tv, time_event_callback, NULL))) {
802 fprintf(stderr, _("time_new() failed.\n"));
803 goto quit;
804 }
805 }
806
807 /* Run the main loop */
808 if (pa_mainloop_run(m, &ret) < 0) {
809 fprintf(stderr, _("pa_mainloop_run() failed.\n"));
810 goto quit;
811 }
812
813 quit:
814 if (stream)
815 pa_stream_unref(stream);
816
817 if (context)
818 pa_context_unref(context);
819
820 if (stdio_event) {
821 assert(mainloop_api);
822 mainloop_api->io_free(stdio_event);
823 }
824
825 if (time_event) {
826 assert(mainloop_api);
827 mainloop_api->time_free(time_event);
828 }
829
830 if (m) {
831 pa_signal_done();
832 pa_mainloop_free(m);
833 }
834
835 pa_xfree(buffer);
836
837 pa_xfree(server);
838 pa_xfree(device);
839 pa_xfree(client_name);
840 pa_xfree(stream_name);
841
842 return ret;
843 }