00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025 #ifdef HAVE_CONFIG_H
00026 #include <config.h>
00027 #endif
00028
00029 #include <signal.h>
00030 #include <string.h>
00031 #include <errno.h>
00032 #include <unistd.h>
00033 #include <assert.h>
00034 #include <stdio.h>
00035 #include <stdlib.h>
00036 #include <getopt.h>
00037 #include <fcntl.h>
00038
00039 #include <pulse/pulseaudio.h>
00040
00041 #define TIME_EVENT_USEC 50000
00042
00043 #if PA_API_VERSION < 9
00044 #error Invalid PulseAudio API version
00045 #endif
00046
00047 static enum { RECORD, PLAYBACK } mode = PLAYBACK;
00048
00049 static pa_context *context = NULL;
00050 static pa_stream *stream = NULL;
00051 static pa_mainloop_api *mainloop_api = NULL;
00052
00053 static void *buffer = NULL;
00054 static size_t buffer_length = 0, buffer_index = 0;
00055
00056 static pa_io_event* stdio_event = NULL;
00057
00058 static char *stream_name = NULL, *client_name = NULL, *device = NULL;
00059
00060 static int verbose = 0;
00061 static pa_volume_t volume = PA_VOLUME_NORM;
00062
00063 static pa_sample_spec sample_spec = {
00064 .format = PA_SAMPLE_S16LE,
00065 .rate = 44100,
00066 .channels = 2
00067 };
00068
00069 static pa_channel_map channel_map;
00070 static int channel_map_set = 0;
00071
00072
00073 static void quit(int ret) {
00074 assert(mainloop_api);
00075 mainloop_api->quit(mainloop_api, ret);
00076 }
00077
00078
00079 static void do_stream_write(size_t length) {
00080 size_t l;
00081 assert(length);
00082
00083 if (!buffer || !buffer_length)
00084 return;
00085
00086 l = length;
00087 if (l > buffer_length)
00088 l = buffer_length;
00089
00090 if (pa_stream_write(stream, (uint8_t*) buffer + buffer_index, l, NULL, 0, PA_SEEK_RELATIVE) < 0) {
00091 fprintf(stderr, "pa_stream_write() failed: %s\n", pa_strerror(pa_context_errno(context)));
00092 quit(1);
00093 return;
00094 }
00095
00096 buffer_length -= l;
00097 buffer_index += l;
00098
00099 if (!buffer_length) {
00100 pa_xfree(buffer);
00101 buffer = NULL;
00102 buffer_index = buffer_length = 0;
00103 }
00104 }
00105
00106
00107 static void stream_write_callback(pa_stream *s, size_t length, void *userdata) {
00108 assert(s && length);
00109
00110 if (stdio_event)
00111 mainloop_api->io_enable(stdio_event, PA_IO_EVENT_INPUT);
00112
00113 if (!buffer)
00114 return;
00115
00116 do_stream_write(length);
00117 }
00118
00119
00120 static void stream_read_callback(pa_stream *s, size_t length, void *userdata) {
00121 const void *data;
00122 assert(s && length);
00123
00124 if (stdio_event)
00125 mainloop_api->io_enable(stdio_event, PA_IO_EVENT_OUTPUT);
00126
00127 if (pa_stream_peek(s, &data, &length) < 0) {
00128 fprintf(stderr, "pa_stream_peek() failed: %s\n", pa_strerror(pa_context_errno(context)));
00129 quit(1);
00130 return;
00131 }
00132
00133 assert(data && length);
00134
00135 if (buffer) {
00136 fprintf(stderr, "Buffer overrun, dropping incoming data\n");
00137 if (pa_stream_drop(s) < 0) {
00138 fprintf(stderr, "pa_stream_drop() failed: %s\n", pa_strerror(pa_context_errno(context)));
00139 quit(1);
00140 }
00141 return;
00142 }
00143
00144 buffer = pa_xmalloc(buffer_length = length);
00145 memcpy(buffer, data, length);
00146 buffer_index = 0;
00147 pa_stream_drop(s);
00148 }
00149
00150
00151 static void stream_state_callback(pa_stream *s, void *userdata) {
00152 assert(s);
00153
00154 switch (pa_stream_get_state(s)) {
00155 case PA_STREAM_CREATING:
00156 case PA_STREAM_TERMINATED:
00157 break;
00158
00159 case PA_STREAM_READY:
00160 if (verbose) {
00161 const pa_buffer_attr *a;
00162
00163 fprintf(stderr, "Stream successfully created.\n");
00164
00165 if (!(a = pa_stream_get_buffer_attr(s)))
00166 fprintf(stderr, "pa_stream_get_buffer_attr() failed: %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s))));
00167 else {
00168
00169 if (mode == PLAYBACK)
00170 fprintf(stderr, "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u\n", a->maxlength, a->tlength, a->prebuf, a->minreq);
00171 else {
00172 assert(mode == RECORD);
00173 fprintf(stderr, "Buffer metrics: maxlength=%u, fragsize=%u\n", a->maxlength, a->fragsize);
00174 }
00175
00176 }
00177
00178 }
00179
00180 break;
00181
00182 case PA_STREAM_FAILED:
00183 default:
00184 fprintf(stderr, "Stream error: %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s))));
00185 quit(1);
00186 }
00187 }
00188
00189
00190 static void context_state_callback(pa_context *c, void *userdata) {
00191 assert(c);
00192
00193 switch (pa_context_get_state(c)) {
00194 case PA_CONTEXT_CONNECTING:
00195 case PA_CONTEXT_AUTHORIZING:
00196 case PA_CONTEXT_SETTING_NAME:
00197 break;
00198
00199 case PA_CONTEXT_READY: {
00200 int r;
00201
00202 assert(c && !stream);
00203
00204 if (verbose)
00205 fprintf(stderr, "Connection established.\n");
00206
00207 if (!(stream = pa_stream_new(c, stream_name, &sample_spec, channel_map_set ? &channel_map : NULL))) {
00208 fprintf(stderr, "pa_stream_new() failed: %s\n", pa_strerror(pa_context_errno(c)));
00209 goto fail;
00210 }
00211
00212 pa_stream_set_state_callback(stream, stream_state_callback, NULL);
00213 pa_stream_set_write_callback(stream, stream_write_callback, NULL);
00214 pa_stream_set_read_callback(stream, stream_read_callback, NULL);
00215
00216 if (mode == PLAYBACK) {
00217 pa_cvolume cv;
00218 if ((r = pa_stream_connect_playback(stream, device, NULL, 0, pa_cvolume_set(&cv, sample_spec.channels, volume), NULL)) < 0) {
00219 fprintf(stderr, "pa_stream_connect_playback() failed: %s\n", pa_strerror(pa_context_errno(c)));
00220 goto fail;
00221 }
00222
00223 } else {
00224 if ((r = pa_stream_connect_record(stream, device, NULL, 0)) < 0) {
00225 fprintf(stderr, "pa_stream_connect_record() failed: %s\n", pa_strerror(pa_context_errno(c)));
00226 goto fail;
00227 }
00228 }
00229
00230 break;
00231 }
00232
00233 case PA_CONTEXT_TERMINATED:
00234 quit(0);
00235 break;
00236
00237 case PA_CONTEXT_FAILED:
00238 default:
00239 fprintf(stderr, "Connection failure: %s\n", pa_strerror(pa_context_errno(c)));
00240 goto fail;
00241 }
00242
00243 return;
00244
00245 fail:
00246 quit(1);
00247
00248 }
00249
00250
00251 static void context_drain_complete(pa_context*c, void *userdata) {
00252 pa_context_disconnect(c);
00253 }
00254
00255
00256 static void stream_drain_complete(pa_stream*s, int success, void *userdata) {
00257 pa_operation *o;
00258
00259 if (!success) {
00260 fprintf(stderr, "Failed to drain stream: %s\n", pa_strerror(pa_context_errno(context)));
00261 quit(1);
00262 }
00263
00264 if (verbose)
00265 fprintf(stderr, "Playback stream drained.\n");
00266
00267 pa_stream_disconnect(stream);
00268 pa_stream_unref(stream);
00269 stream = NULL;
00270
00271 if (!(o = pa_context_drain(context, context_drain_complete, NULL)))
00272 pa_context_disconnect(context);
00273 else {
00274 if (verbose)
00275 fprintf(stderr, "Draining connection to server.\n");
00276 }
00277 }
00278
00279
00280 static void stdin_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
00281 size_t l, w = 0;
00282 ssize_t r;
00283 assert(a == mainloop_api && e && stdio_event == e);
00284
00285 if (buffer) {
00286 mainloop_api->io_enable(stdio_event, PA_IO_EVENT_NULL);
00287 return;
00288 }
00289
00290 if (!stream || pa_stream_get_state(stream) != PA_STREAM_READY || !(l = w = pa_stream_writable_size(stream)))
00291 l = 4096;
00292
00293 buffer = pa_xmalloc(l);
00294
00295 if ((r = read(fd, buffer, l)) <= 0) {
00296 if (r == 0) {
00297 if (verbose)
00298 fprintf(stderr, "Got EOF.\n");
00299
00300 if (stream) {
00301 pa_operation *o;
00302
00303 if (!(o = pa_stream_drain(stream, stream_drain_complete, NULL))) {
00304 fprintf(stderr, "pa_stream_drain(): %s\n", pa_strerror(pa_context_errno(context)));
00305 quit(1);
00306 return;
00307 }
00308
00309 pa_operation_unref(o);
00310 } else
00311 quit(0);
00312
00313 } else {
00314 fprintf(stderr, "read() failed: %s\n", strerror(errno));
00315 quit(1);
00316 }
00317
00318 mainloop_api->io_free(stdio_event);
00319 stdio_event = NULL;
00320 return;
00321 }
00322
00323 buffer_length = r;
00324 buffer_index = 0;
00325
00326 if (w)
00327 do_stream_write(w);
00328 }
00329
00330
00331 static void stdout_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
00332 ssize_t r;
00333 assert(a == mainloop_api && e && stdio_event == e);
00334
00335 if (!buffer) {
00336 mainloop_api->io_enable(stdio_event, PA_IO_EVENT_NULL);
00337 return;
00338 }
00339
00340 assert(buffer_length);
00341
00342 if ((r = write(fd, (uint8_t*) buffer+buffer_index, buffer_length)) <= 0) {
00343 fprintf(stderr, "write() failed: %s\n", strerror(errno));
00344 quit(1);
00345
00346 mainloop_api->io_free(stdio_event);
00347 stdio_event = NULL;
00348 return;
00349 }
00350
00351 buffer_length -= r;
00352 buffer_index += r;
00353
00354 if (!buffer_length) {
00355 pa_xfree(buffer);
00356 buffer = NULL;
00357 buffer_length = buffer_index = 0;
00358 }
00359 }
00360
00361
00362 static void exit_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) {
00363 if (verbose)
00364 fprintf(stderr, "Got signal, exiting.\n");
00365 quit(0);
00366 }
00367
00368
00369 static void stream_update_timing_callback(pa_stream *s, int success, void *userdata) {
00370 pa_usec_t latency, usec;
00371 int negative = 0;
00372
00373 assert(s);
00374
00375 if (!success ||
00376 pa_stream_get_time(s, &usec) < 0 ||
00377 pa_stream_get_latency(s, &latency, &negative) < 0) {
00378 fprintf(stderr, "Failed to get latency: %s\n", pa_strerror(pa_context_errno(context)));
00379 quit(1);
00380 return;
00381 }
00382
00383 fprintf(stderr, "Time: %0.3f sec; Latency: %0.0f usec. \r",
00384 (float) usec / 1000000,
00385 (float) latency * (negative?-1:1));
00386 }
00387
00388
00389 static void sigusr1_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) {
00390
00391 if (!stream)
00392 return;
00393
00394 pa_operation_unref(pa_stream_update_timing_info(stream, stream_update_timing_callback, NULL));
00395 }
00396
00397 static void time_event_callback(pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata) {
00398 struct timeval next;
00399
00400 if (stream && pa_stream_get_state(stream) == PA_STREAM_READY) {
00401 pa_operation *o;
00402 if (!(o = pa_stream_update_timing_info(stream, stream_update_timing_callback, NULL)))
00403 fprintf(stderr, "pa_stream_update_timing_info() failed: %s\n", pa_strerror(pa_context_errno(context)));
00404 else
00405 pa_operation_unref(o);
00406 }
00407
00408 pa_gettimeofday(&next);
00409 pa_timeval_add(&next, TIME_EVENT_USEC);
00410
00411 m->time_restart(e, &next);
00412 }
00413
00414 static void help(const char *argv0) {
00415
00416 printf("%s [options]\n\n"
00417 " -h, --help Show this help\n"
00418 " --version Show version\n\n"
00419 " -r, --record Create a connection for recording\n"
00420 " -p, --playback Create a connection for playback\n\n"
00421 " -v, --verbose Enable verbose operations\n\n"
00422 " -s, --server=SERVER The name of the server to connect to\n"
00423 " -d, --device=DEVICE The name of the sink/source to connect to\n"
00424 " -n, --client-name=NAME How to call this client on the server\n"
00425 " --stream-name=NAME How to call this stream on the server\n"
00426 " --volume=VOLUME Specify the initial (linear) volume in range 0...65536\n"
00427 " --rate=SAMPLERATE The sample rate in Hz (defaults to 44100)\n"
00428 " --format=SAMPLEFORMAT The sample type, one of s16le, s16be, u8, float32le,\n"
00429 " float32be, ulaw, alaw (defaults to s16ne)\n"
00430 " --channels=CHANNELS The number of channels, 1 for mono, 2 for stereo\n"
00431 " (defaults to 2)\n"
00432 " --channel-map=CHANNELMAP Channel map to use instead of the default\n",
00433 argv0);
00434 }
00435
00436 enum {
00437 ARG_VERSION = 256,
00438 ARG_STREAM_NAME,
00439 ARG_VOLUME,
00440 ARG_SAMPLERATE,
00441 ARG_SAMPLEFORMAT,
00442 ARG_CHANNELS,
00443 ARG_CHANNELMAP,
00444 };
00445
00446 int main(int argc, char *argv[]) {
00447 pa_mainloop* m = NULL;
00448 int ret = 1, r, c;
00449 char *bn, *server = NULL;
00450 pa_time_event *time_event = NULL;
00451
00452 static const struct option long_options[] = {
00453 {"record", 0, NULL, 'r'},
00454 {"playback", 0, NULL, 'p'},
00455 {"device", 1, NULL, 'd'},
00456 {"server", 1, NULL, 's'},
00457 {"client-name", 1, NULL, 'n'},
00458 {"stream-name", 1, NULL, ARG_STREAM_NAME},
00459 {"version", 0, NULL, ARG_VERSION},
00460 {"help", 0, NULL, 'h'},
00461 {"verbose", 0, NULL, 'v'},
00462 {"volume", 1, NULL, ARG_VOLUME},
00463 {"rate", 1, NULL, ARG_SAMPLERATE},
00464 {"format", 1, NULL, ARG_SAMPLEFORMAT},
00465 {"channels", 1, NULL, ARG_CHANNELS},
00466 {"channel-map", 1, NULL, ARG_CHANNELMAP},
00467 {NULL, 0, NULL, 0}
00468 };
00469
00470 if (!(bn = strrchr(argv[0], '/')))
00471 bn = argv[0];
00472 else
00473 bn++;
00474
00475 if (strstr(bn, "rec") || strstr(bn, "mon"))
00476 mode = RECORD;
00477 else if (strstr(bn, "cat") || strstr(bn, "play"))
00478 mode = PLAYBACK;
00479
00480 while ((c = getopt_long(argc, argv, "rpd:s:n:hv", long_options, NULL)) != -1) {
00481
00482 switch (c) {
00483 case 'h' :
00484 help(bn);
00485 ret = 0;
00486 goto quit;
00487
00488 case ARG_VERSION:
00489 printf("pacat "PACKAGE_VERSION"\nCompiled with libpulse %s\nLinked with libpulse %s\n", pa_get_headers_version(), pa_get_library_version());
00490 ret = 0;
00491 goto quit;
00492
00493 case 'r':
00494 mode = RECORD;
00495 break;
00496
00497 case 'p':
00498 mode = PLAYBACK;
00499 break;
00500
00501 case 'd':
00502 pa_xfree(device);
00503 device = pa_xstrdup(optarg);
00504 break;
00505
00506 case 's':
00507 pa_xfree(server);
00508 server = pa_xstrdup(optarg);
00509 break;
00510
00511 case 'n':
00512 pa_xfree(client_name);
00513 client_name = pa_xstrdup(optarg);
00514 break;
00515
00516 case ARG_STREAM_NAME:
00517 pa_xfree(stream_name);
00518 stream_name = pa_xstrdup(optarg);
00519 break;
00520
00521 case 'v':
00522 verbose = 1;
00523 break;
00524
00525 case ARG_VOLUME: {
00526 int v = atoi(optarg);
00527 volume = v < 0 ? 0 : v;
00528 break;
00529 }
00530
00531 case ARG_CHANNELS:
00532 sample_spec.channels = atoi(optarg);
00533 break;
00534
00535 case ARG_SAMPLEFORMAT:
00536 sample_spec.format = pa_parse_sample_format(optarg);
00537 break;
00538
00539 case ARG_SAMPLERATE:
00540 sample_spec.rate = atoi(optarg);
00541 break;
00542
00543 case ARG_CHANNELMAP:
00544 if (!pa_channel_map_parse(&channel_map, optarg)) {
00545 fprintf(stderr, "Invalid channel map\n");
00546 goto quit;
00547 }
00548
00549 channel_map_set = 1;
00550 break;
00551
00552 default:
00553 goto quit;
00554 }
00555 }
00556
00557 if (!pa_sample_spec_valid(&sample_spec)) {
00558 fprintf(stderr, "Invalid sample specification\n");
00559 goto quit;
00560 }
00561
00562 if (channel_map_set && channel_map.channels != sample_spec.channels) {
00563 fprintf(stderr, "Channel map doesn't match sample specification\n");
00564 goto quit;
00565 }
00566
00567 if (verbose) {
00568 char t[PA_SAMPLE_SPEC_SNPRINT_MAX];
00569 pa_sample_spec_snprint(t, sizeof(t), &sample_spec);
00570 fprintf(stderr, "Opening a %s stream with sample specification '%s'.\n", mode == RECORD ? "recording" : "playback", t);
00571 }
00572
00573 if (!(optind >= argc)) {
00574 if (optind+1 == argc) {
00575 int fd;
00576
00577 if ((fd = open(argv[optind], mode == PLAYBACK ? O_RDONLY : O_WRONLY|O_TRUNC|O_CREAT, 0666)) < 0) {
00578 fprintf(stderr, "open(): %s\n", strerror(errno));
00579 goto quit;
00580 }
00581
00582 if (dup2(fd, mode == PLAYBACK ? 0 : 1) < 0) {
00583 fprintf(stderr, "dup2(): %s\n", strerror(errno));
00584 goto quit;
00585 }
00586
00587 close(fd);
00588
00589 if (!stream_name)
00590 stream_name = pa_xstrdup(argv[optind]);
00591
00592 } else {
00593 fprintf(stderr, "Too many arguments.\n");
00594 goto quit;
00595 }
00596 }
00597
00598 if (!client_name)
00599 client_name = pa_xstrdup(bn);
00600
00601 if (!stream_name)
00602 stream_name = pa_xstrdup(client_name);
00603
00604
00605 if (!(m = pa_mainloop_new())) {
00606 fprintf(stderr, "pa_mainloop_new() failed.\n");
00607 goto quit;
00608 }
00609
00610 mainloop_api = pa_mainloop_get_api(m);
00611
00612 r = pa_signal_init(mainloop_api);
00613 assert(r == 0);
00614 pa_signal_new(SIGINT, exit_signal_callback, NULL);
00615 pa_signal_new(SIGTERM, exit_signal_callback, NULL);
00616 #ifdef SIGUSR1
00617 pa_signal_new(SIGUSR1, sigusr1_signal_callback, NULL);
00618 #endif
00619 #ifdef SIGPIPE
00620 signal(SIGPIPE, SIG_IGN);
00621 #endif
00622
00623 if (!(stdio_event = mainloop_api->io_new(mainloop_api,
00624 mode == PLAYBACK ? STDIN_FILENO : STDOUT_FILENO,
00625 mode == PLAYBACK ? PA_IO_EVENT_INPUT : PA_IO_EVENT_OUTPUT,
00626 mode == PLAYBACK ? stdin_callback : stdout_callback, NULL))) {
00627 fprintf(stderr, "io_new() failed.\n");
00628 goto quit;
00629 }
00630
00631
00632 if (!(context = pa_context_new(mainloop_api, client_name))) {
00633 fprintf(stderr, "pa_context_new() failed.\n");
00634 goto quit;
00635 }
00636
00637 pa_context_set_state_callback(context, context_state_callback, NULL);
00638
00639
00640 pa_context_connect(context, server, 0, NULL);
00641
00642 if (verbose) {
00643 struct timeval tv;
00644
00645 pa_gettimeofday(&tv);
00646 pa_timeval_add(&tv, TIME_EVENT_USEC);
00647
00648 if (!(time_event = mainloop_api->time_new(mainloop_api, &tv, time_event_callback, NULL))) {
00649 fprintf(stderr, "time_new() failed.\n");
00650 goto quit;
00651 }
00652 }
00653
00654
00655 if (pa_mainloop_run(m, &ret) < 0) {
00656 fprintf(stderr, "pa_mainloop_run() failed.\n");
00657 goto quit;
00658 }
00659
00660 quit:
00661 if (stream)
00662 pa_stream_unref(stream);
00663
00664 if (context)
00665 pa_context_unref(context);
00666
00667 if (stdio_event) {
00668 assert(mainloop_api);
00669 mainloop_api->io_free(stdio_event);
00670 }
00671
00672 if (time_event) {
00673 assert(mainloop_api);
00674 mainloop_api->time_free(time_event);
00675 }
00676
00677 if (m) {
00678 pa_signal_done();
00679 pa_mainloop_free(m);
00680 }
00681
00682 pa_xfree(buffer);
00683
00684 pa_xfree(server);
00685 pa_xfree(device);
00686 pa_xfree(client_name);
00687 pa_xfree(stream_name);
00688
00689 return ret;
00690 }