]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * pipe output driver. This file is part of Shairport. | |
3 | * Copyright (c) James Laird 2013 | |
4 | * All rights reserved. | |
5 | * | |
6 | * Modifications for audio synchronisation | |
7 | * and related work, copyright (c) Mike Brady 2014 | |
8 | * All rights reserved. | |
9 | * | |
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 | ||
31 | #include "audio.h" | |
32 | #include "common.h" | |
33 | #include <errno.h> | |
34 | #include <fcntl.h> | |
35 | #include <memory.h> | |
36 | #include <stdio.h> | |
37 | #include <stdlib.h> | |
38 | #include <sys/stat.h> | |
39 | #include <sys/types.h> | |
40 | #include <unistd.h> | |
41 | ||
42 | static int fd = -1; | |
43 | ||
44 | char *pipename = NULL; | |
45 | int warned = 0; | |
46 | ||
47 | static void start(__attribute__((unused)) int sample_rate, | |
48 | __attribute__((unused)) int sample_format) { | |
49 | // this will leave fd as -1 if a reader hasn't been attached | |
50 | fd = open(pipename, O_WRONLY | O_NONBLOCK); | |
51 | if ((fd < -1) && (warned == 0)) { | |
52 | warn("Error %d opening the pipe named \"%s\".", errno, pipename); | |
53 | warned = 1; | |
54 | } | |
55 | } | |
56 | ||
57 | static int play(void *buf, int samples) { | |
58 | // if the file is not open, try to open it. | |
59 | char errorstring[1024]; | |
60 | if (fd == -1) { | |
61 | fd = open(pipename, O_WRONLY | O_NONBLOCK); | |
62 | } | |
63 | // if it's got a reader, write to it. | |
64 | if (fd > 0) { | |
65 | int rc = non_blocking_write(fd, buf, samples * 4); | |
66 | if ((rc < 0) && (warned == 0)) { | |
67 | strerror_r(errno, (char *)errorstring, 1024); | |
68 | warn("Error %d writing to the pipe named \"%s\": \"%s\".", errno, pipename, errorstring); | |
69 | warned = 1; | |
70 | } | |
71 | } else if ((fd == -1) && (warned == 0)) { | |
72 | strerror_r(errno, (char *)errorstring, 1024); | |
73 | warn("Error %d opening the pipe named \"%s\": \"%s\".", errno, pipename, errorstring); | |
74 | warned = 1; | |
75 | } | |
76 | return warned; | |
77 | } | |
78 | ||
79 | static void stop(void) { | |
80 | // Don't close the pipe just because a play session has stopped. | |
81 | // if (fd > 0) | |
82 | // close(fd); | |
83 | } | |
84 | ||
85 | static int init(int argc, char **argv) { | |
86 | debug(1, "pipe init"); | |
87 | // const char *str; | |
88 | // int value; | |
89 | // double dvalue; | |
90 | ||
91 | // set up default values first | |
92 | ||
93 | config.audio_backend_buffer_desired_length = 1.0; | |
94 | config.audio_backend_latency_offset = 0; | |
95 | ||
96 | // do the "general" audio options. Note, these options are in the "general" stanza! | |
97 | parse_general_audio_options(); | |
98 | ||
99 | if (config.cfg != NULL) { | |
100 | /* Get the Output Pipename. */ | |
101 | const char *str; | |
102 | if (config_lookup_string(config.cfg, "pipe.name", &str)) { | |
103 | pipename = (char *)str; | |
104 | } | |
105 | ||
106 | if ((pipename) && (strcasecmp(pipename, "STDOUT") == 0)) | |
107 | die("Can't use \"pipe\" backend for STDOUT. Use the \"stdout\" backend instead."); | |
108 | } | |
109 | ||
110 | if ((pipename == NULL) && (argc != 1)) | |
111 | die("bad or missing argument(s) to pipe"); | |
112 | ||
113 | if (argc == 1) | |
114 | pipename = strdup(argv[0]); | |
115 | ||
116 | // here, create the pipe | |
117 | if (mkfifo(pipename, 0644) && errno != EEXIST) | |
118 | die("Could not create output pipe \"%s\"", pipename); | |
119 | ||
120 | debug(1, "Pipename is \"%s\"", pipename); | |
121 | ||
122 | return 0; | |
123 | } | |
124 | ||
125 | static void deinit(void) { | |
126 | if (fd > 0) | |
127 | close(fd); | |
128 | } | |
129 | ||
130 | static void help(void) { printf(" specify the pathname of the pipe to write to.\n"); } | |
131 | ||
132 | audio_output audio_pipe = {.name = "pipe", | |
133 | .help = &help, | |
134 | .init = &init, | |
135 | .deinit = &deinit, | |
136 | .start = &start, | |
137 | .stop = &stop, | |
138 | .is_running = NULL, | |
139 | .flush = NULL, | |
140 | .delay = NULL, | |
141 | .play = &play, | |
142 | .volume = NULL, | |
143 | .parameters = NULL, | |
144 | .mute = NULL}; |