]>
Commit | Line | Data |
---|---|---|
0129db08 JL |
1 | /* |
2 | * pipe output driver. This file is part of Shairport. | |
3 | * Copyright (c) James Laird 2013 | |
4 | * All rights reserved. | |
5 | * | |
018537e8 MB |
6 | * Modifications for audio synchronisation |
7 | * and related work, copyright (c) Mike Brady 2014 | |
8 | * All rights reserved. | |
9 | * | |
0129db08 JL |
10 | * Permission is hereby granted, free of charge, to any person |
11 | * obtaining a copy of this software and associated documentation | |
12 | * files (the "Software"), to deal in the Software without | |
13 | * restriction, including without limitation the rights to use, | |
14 | * copy, modify, merge, publish, distribute, sublicense, and/or | |
15 | * sell copies of the Software, and to permit persons to whom the | |
16 | * Software is furnished to do so, subject to the following conditions: | |
17 | * | |
18 | * The above copyright notice and this permission notice shall be | |
19 | * included in all copies or substantial portions of the Software. | |
20 | * | |
21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
22 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES | |
23 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
24 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | |
25 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | |
26 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
27 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
28 | * OTHER DEALINGS IN THE SOFTWARE. | |
29 | */ | |
30 | ||
064bd293 MB |
31 | #include "audio.h" |
32 | #include "common.h" | |
33 | #include <errno.h> | |
0129db08 JL |
34 | #include <fcntl.h> |
35 | #include <memory.h> | |
064bd293 | 36 | #include <stdio.h> |
2e27c885 | 37 | #include <stdlib.h> |
0cc29d63 | 38 | #include <sys/stat.h> |
064bd293 MB |
39 | #include <sys/types.h> |
40 | #include <unistd.h> | |
0129db08 | 41 | |
28a5afc9 | 42 | static int fd = -1; |
0129db08 JL |
43 | |
44 | char *pipename = NULL; | |
485e18db | 45 | char *default_pipe_name = "/tmp/shairport-sync-audio"; |
0129db08 | 46 | |
0cd8a2ce MB |
47 | static void start(__attribute__((unused)) int sample_rate, |
48 | __attribute__((unused)) int sample_format) { | |
c8b0be30 | 49 | |
4c57ca08 MB |
50 | // this will leave fd as -1 if a reader hasn't been attached to the pipe |
51 | // we check that it's not a "real" error though. From the "man 2 open" page: | |
c8b0be30 MB |
52 | // "ENXIO O_NONBLOCK | O_WRONLY is set, the named file is a FIFO, and no process has the FIFO |
53 | // open for reading." | |
9e34b133 | 54 | |
ca562872 MB |
55 | fd = try_to_open_pipe_for_writing(pipename); |
56 | // we check that it's not a "real" error. From the "man 2 open" page: | |
57 | // "ENXIO O_NONBLOCK | O_WRONLY is set, the named file is a FIFO, and no process has the FIFO | |
58 | // open for reading." Which is okay. | |
59 | if ((fd == -1) && (errno != ENXIO)) { | |
60 | char errorstring[1024]; | |
61 | strerror_r(errno, (char *)errorstring, sizeof(errorstring)); | |
62 | debug(1, "audio_pipe start -- error %d (\"%s\") opening pipe: \"%s\".", errno, | |
63 | (char *)errorstring, pipename); | |
64 | warn("can not open audio pipe -- error %d (\"%s\") opening pipe: \"%s\".", errno, | |
65 | (char *)errorstring, pipename); | |
66 | } | |
dfd90bac | 67 | } |
0129db08 | 68 | |
fd880056 MB |
69 | static int play(void *buf, int samples, __attribute__((unused)) int sample_type, |
70 | __attribute__((unused)) uint32_t timestamp, | |
71 | __attribute__((unused)) uint64_t playtime) { | |
fdbf64e9 | 72 | // if the file is not open, try to open it. |
ec0bc81b | 73 | char errorstring[1024]; |
fdbf64e9 | 74 | if (fd == -1) { |
ca562872 | 75 | fd = try_to_open_pipe_for_writing(pipename); |
fdbf64e9 MB |
76 | } |
77 | // if it's got a reader, write to it. | |
ec0bc81b | 78 | if (fd > 0) { |
ca562872 | 79 | // int rc = non_blocking_write(fd, buf, samples * 4); |
9e34b133 MB |
80 | int rc = write(fd, buf, samples * 4); |
81 | if ((rc < 0) && (errno != EPIPE)) { | |
e513e533 | 82 | strerror_r(errno, (char *)errorstring, 1024); |
ca562872 MB |
83 | debug(1, "audio_pip play: error %d writing to the pipe named \"%s\": \"%s\".", errno, |
84 | pipename, errorstring); | |
ec0bc81b | 85 | } |
e513e533 | 86 | } |
9e34b133 | 87 | return 0; |
fdbf64e9 | 88 | } |
0129db08 JL |
89 | |
90 | static void stop(void) { | |
064bd293 | 91 | // Don't close the pipe just because a play session has stopped. |
0129db08 JL |
92 | } |
93 | ||
54250f75 | 94 | static int init(int argc, char **argv) { |
9e34b133 | 95 | // debug(1, "pipe init"); |
c36a7822 MB |
96 | // const char *str; |
97 | // int value; | |
98 | // double dvalue; | |
ae84366e | 99 | |
b7864b4e MB |
100 | // set up default values first |
101 | ||
ae84366e | 102 | config.audio_backend_buffer_desired_length = 1.0; |
54250f75 | 103 | config.audio_backend_latency_offset = 0; |
e513e533 | 104 | |
b7864b4e MB |
105 | // do the "general" audio options. Note, these options are in the "general" stanza! |
106 | parse_general_audio_options(); | |
0129db08 | 107 | |
87a0475c MB |
108 | if (config.cfg != NULL) { |
109 | /* Get the Output Pipename. */ | |
110 | const char *str; | |
111 | if (config_lookup_string(config.cfg, "pipe.name", &str)) { | |
112 | pipename = (char *)str; | |
113 | } | |
4cd3f5c3 | 114 | } |
b7864b4e | 115 | |
485e18db MB |
116 | if (argc > 1) |
117 | die("too many command-line arguments to pipe"); | |
4cd3f5c3 | 118 | |
87a0475c | 119 | if (argc == 1) |
485e18db MB |
120 | pipename = argv[0]; // command line argument has priority |
121 | ||
3fc95704 MB |
122 | if ((pipename) && (strcasecmp(pipename, "STDOUT") == 0)) |
123 | die("Can't use \"pipe\" backend for STDOUT. Use the \"stdout\" backend instead."); | |
485e18db | 124 | |
3fc95704 MB |
125 | if (pipename == NULL) |
126 | pipename = default_pipe_name; // if none specified | |
064bd293 | 127 | |
87a0475c | 128 | // here, create the pipe |
34659538 | 129 | mode_t oldumask = umask(000); |
06c80713 | 130 | if (mkfifo(pipename, 0666) && errno != EEXIST) |
34659538 MB |
131 | die("Could not create audio pipe \"%s\"", pipename); |
132 | umask(oldumask); | |
4cd3f5c3 | 133 | |
34659538 | 134 | debug(1, "audio pipe name is \"%s\"", pipename); |
0129db08 | 135 | |
87a0475c | 136 | return 0; |
0129db08 JL |
137 | } |
138 | ||
139 | static void deinit(void) { | |
064bd293 | 140 | if (fd > 0) |
87a0475c | 141 | close(fd); |
0129db08 JL |
142 | } |
143 | ||
e8b8c6ed MB |
144 | static void help(void) { |
145 | printf(" Provide the pipe's pathname. The default is \"%s\".\n", default_pipe_name); | |
146 | } | |
0129db08 | 147 | |
87a0475c MB |
148 | audio_output audio_pipe = {.name = "pipe", |
149 | .help = &help, | |
150 | .init = &init, | |
151 | .deinit = &deinit, | |
83c0405d | 152 | .prepare = NULL, |
87a0475c MB |
153 | .start = &start, |
154 | .stop = &stop, | |
8cabb16f | 155 | .is_running = NULL, |
87a0475c MB |
156 | .flush = NULL, |
157 | .delay = NULL, | |
cd9da86f | 158 | .stats = NULL, |
87a0475c MB |
159 | .play = &play, |
160 | .volume = NULL, | |
4c70223f MB |
161 | .parameters = NULL, |
162 | .mute = NULL}; |