2 * Copyright (C) 2012-2013 Juho Vähä-Herttua
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
12 * The above copyright notice and this permission notice shall be included
13 * in all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 #include <shairplay/dnssd.h>
36 #include <shairplay/raop.h>
48 char ao_devicename
[56];
50 } shairplay_options_t
;
60 } shairplay_session_t
;
69 signal_handler(int sig
)
81 struct sigaction sigact
;
83 sigact
.sa_handler
= signal_handler
;
84 sigemptyset(&sigact
.sa_mask
);
86 sigaction(SIGINT
, &sigact
, NULL
);
87 sigaction(SIGTERM
, &sigact
, NULL
);
94 audio_open_device(shairplay_options_t
*opt
, int bits
, int channels
, int samplerate
)
96 ao_device
*device
= NULL
;
97 ao_option
*ao_options
= NULL
;
98 ao_sample_format format
;
101 /* Get the libao driver ID */
102 if (strlen(opt
->ao_driver
)) {
103 driver_id
= ao_driver_id(opt
->ao_driver
);
105 driver_id
= ao_default_driver_id();
108 /* Add all available libao options */
109 ao_append_option(&ao_options
, "client_name", opt
->apname
);
110 if (strlen(opt
->ao_devicename
)) {
111 ao_append_option(&ao_options
, "dev", opt
->ao_devicename
);
113 if (strlen(opt
->ao_deviceid
)) {
114 ao_append_option(&ao_options
, "id", opt
->ao_deviceid
);
117 /* Set audio format */
118 memset(&format
, 0, sizeof(format
));
120 format
.channels
= channels
;
121 format
.rate
= samplerate
;
122 format
.byte_format
= AO_FMT_NATIVE
;
124 /* Try opening the actual device */
125 device
= ao_open_live(driver_id
, &format
, ao_options
);
126 ao_free_options(ao_options
);
131 audio_init(void *cls
, int bits
, int channels
, int samplerate
)
133 shairplay_options_t
*options
= cls
;
134 shairplay_session_t
*session
;
136 session
= calloc(1, sizeof(shairplay_session_t
));
139 session
->device
= audio_open_device(options
, bits
, channels
, samplerate
);
140 if (session
->device
== NULL
) {
141 printf("Error opening device %d\n", errno
);
143 assert(session
->device
);
145 session
->buffering
= 1;
146 session
->volume
= 1.0f
;
151 audio_output(shairplay_session_t
*session
, const void *buffer
, int buflen
)
157 tmpbuflen
= (buflen
> sizeof(tmpbuf
)) ? sizeof(tmpbuf
) : buflen
;
158 memcpy(tmpbuf
, buffer
, tmpbuflen
);
159 if (ao_is_big_endian()) {
160 for (i
=0; i
<tmpbuflen
/2; i
++) {
161 char tmpch
= tmpbuf
[i
*2];
162 tmpbuf
[i
*2] = tmpbuf
[i
*2+1];
163 tmpbuf
[i
*2+1] = tmpch
;
166 shortbuf
= (short *)tmpbuf
;
167 for (i
=0; i
<tmpbuflen
/2; i
++) {
168 shortbuf
[i
] = shortbuf
[i
] * session
->volume
;
170 ao_play(session
->device
, tmpbuf
, tmpbuflen
);
175 audio_process(void *cls
, void *opaque
, const void *buffer
, int buflen
)
177 shairplay_session_t
*session
= opaque
;
180 if (session
->buffering
) {
181 printf("Buffering...\n");
182 if (session
->buflen
+buflen
< sizeof(session
->buffer
)) {
183 memcpy(session
->buffer
+session
->buflen
, buffer
, buflen
);
184 session
->buflen
+= buflen
;
187 session
->buffering
= 0;
188 printf("Finished buffering...\n");
191 while (processed
< session
->buflen
) {
192 processed
+= audio_output(session
,
193 session
->buffer
+processed
,
194 session
->buflen
-processed
);
200 while (processed
< buflen
) {
201 processed
+= audio_output(session
,
208 audio_destroy(void *cls
, void *opaque
)
210 shairplay_session_t
*session
= opaque
;
212 ao_close(session
->device
);
217 audio_set_volume(void *cls
, void *opaque
, float volume
)
219 shairplay_session_t
*session
= opaque
;
220 session
->volume
= pow(10.0, 0.05*volume
);
224 parse_options(shairplay_options_t
*opt
, int argc
, char *argv
[])
226 char *path
= argv
[0];
229 /* Set default values for apname and port */
230 strncpy(opt
->apname
, "Shairplay", sizeof(opt
->apname
)-1);
233 while ((arg
= *++argv
)) {
234 if (!strcmp(arg
, "-a")) {
235 strncpy(opt
->apname
, *++argv
, sizeof(opt
->apname
)-1);
236 } else if (!strncmp(arg
, "--apname=", 9)) {
237 strncpy(opt
->apname
, arg
+9, sizeof(opt
->apname
)-1);
238 } else if (!strcmp(arg
, "-p")) {
239 strncpy(opt
->password
, *++argv
, sizeof(opt
->password
)-1);
240 } else if (!strncmp(arg
, "--password=", 11)) {
241 strncpy(opt
->password
, arg
+11, sizeof(opt
->password
)-1);
242 } else if (!strcmp(arg
, "-o")) {
243 opt
->port
= atoi(*++argv
);
244 } else if (!strncmp(arg
, "--server_port=", 14)) {
245 opt
->port
= atoi(arg
+14);
246 } else if (!strncmp(arg
, "--ao_driver=", 12)) {
247 strncpy(opt
->ao_driver
, arg
+12, sizeof(opt
->ao_driver
)-1);
248 } else if (!strncmp(arg
, "--ao_devicename=", 16)) {
249 strncpy(opt
->ao_devicename
, arg
+16, sizeof(opt
->ao_devicename
)-1);
250 } else if (!strncmp(arg
, "--ao_deviceid=", 14)) {
251 strncpy(opt
->ao_deviceid
, arg
+14, sizeof(opt
->ao_deviceid
)-1);
252 } else if (!strcmp(arg
, "-h") || !strcmp(arg
, "--help")) {
253 fprintf(stderr
, "Shairplay version %s\n", VERSION
);
254 fprintf(stderr
, "Usage: %s [OPTION...]\n", path
);
255 fprintf(stderr
, "\n");
256 fprintf(stderr
, " -a, --apname=AirPort Sets Airport name\n");
257 fprintf(stderr
, " -p, --password=secret Sets password\n");
258 fprintf(stderr
, " -o, --server_port=5000 Sets port for RAOP service\n");
259 fprintf(stderr
, " --ao_driver=driver Sets the ao driver (optional)\n");
260 fprintf(stderr
, " --ao_devicename=devicename Sets the ao device name (optional)\n");
261 fprintf(stderr
, " --ao_deviceid=id Sets the ao device id (optional)\n");
262 fprintf(stderr
, " -h, --help This help\n");
263 fprintf(stderr
, "\n");
272 main(int argc
, char *argv
[])
274 const char hwaddr
[] = { 0x48, 0x5d, 0x60, 0x7c, 0xee, 0x22 };
276 shairplay_options_t options
;
277 ao_device
*device
= NULL
;
281 raop_callbacks_t raop_cbs
;
282 char *password
= NULL
;
290 memset(&options
, 0, sizeof(options
));
291 if (parse_options(&options
, argc
, argv
)) {
297 device
= audio_open_device(&options
, 16, 2, 44100);
298 if (device
== NULL
) {
299 fprintf(stderr
, "Error opening audio device %d\n", errno
);
300 fprintf(stderr
, "Please check your libao settings and try again\n");
307 memset(&raop_cbs
, 0, sizeof(raop_cbs
));
308 raop_cbs
.cls
= &options
;
309 raop_cbs
.audio_init
= audio_init
;
310 raop_cbs
.audio_process
= audio_process
;
311 raop_cbs
.audio_destroy
= audio_destroy
;
312 raop_cbs
.audio_set_volume
= audio_set_volume
;
314 raop
= raop_init_from_keyfile(10, &raop_cbs
, "airport.key", NULL
);
316 fprintf(stderr
, "Could not initialize the RAOP service\n");
320 if (strlen(options
.password
)) {
321 password
= options
.password
;
323 raop_set_log_level(raop
, RAOP_LOG_DEBUG
);
324 raop_start(raop
, &options
.port
, hwaddr
, sizeof(hwaddr
), password
);
327 dnssd
= dnssd_init(&error
);
329 fprintf(stderr
, "ERROR: Could not initialize dnssd library!\n");
330 fprintf(stderr
, "------------------------------------------\n");
331 fprintf(stderr
, "You could try the following resolutions based on your OS:\n");
332 fprintf(stderr
, "Windows: Try installing http://support.apple.com/kb/DL999\n");
333 fprintf(stderr
, "Debian/Ubuntu: Try installing libavahi-compat-libdnssd-dev package\n");
338 dnssd_register_raop(dnssd
, options
.apname
, options
.port
, hwaddr
, sizeof(hwaddr
), 0);
349 dnssd_unregister_raop(dnssd
);
350 dnssd_destroy(dnssd
);