+/**
+ * Copyright (C) 2012-2013 Juho Vähä-Herttua
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <assert.h>
#ifdef WIN32
-#include <windows.h>
+# include <windows.h>
#endif
#include <shairplay/dnssd.h>
char apname[56];
char password[56];
unsigned short port;
+ char hwaddr[6];
char ao_driver[56];
char ao_devicename[56];
} shairplay_session_t;
+static int running;
+
+#ifndef WIN32
+
+#include <signal.h>
+static void
+signal_handler(int sig)
+{
+ switch (sig) {
+ case SIGINT:
+ case SIGTERM:
+ running = 0;
+ break;
+ }
+}
+static void
+init_signals(void)
+{
+ struct sigaction sigact;
+
+ sigact.sa_handler = signal_handler;
+ sigemptyset(&sigact.sa_mask);
+ sigact.sa_flags = 0;
+ sigaction(SIGINT, &sigact, NULL);
+ sigaction(SIGTERM, &sigact, NULL);
+}
+
+#endif
+
+
+static int
+parse_hwaddr(const char *str, char *hwaddr, int hwaddrlen)
+{
+ int slen, i;
+
+ slen = 3*hwaddrlen-1;
+ if (strlen(str) != slen) {
+ return 1;
+ }
+ for (i=0; i<slen; i++) {
+ if (str[i] == ':' && (i%3 == 2)) {
+ continue;
+ }
+ if (str[i] >= '0' && str[i] <= '9') {
+ continue;
+ }
+ if (str[i] >= 'a' && str[i] <= 'f') {
+ continue;
+ }
+ return 1;
+ }
+ for (i=0; i<hwaddrlen; i++) {
+ hwaddr[i] = (char) strtol(str+(i*3), NULL, 16);
+ }
+ return 0;
+}
+
static ao_device *
audio_open_device(shairplay_options_t *opt, int bits, int channels, int samplerate)
{
}
/* Add all available libao options */
- ao_append_option(&ao_options, "client_name", opt->apname);
if (strlen(opt->ao_devicename)) {
ao_append_option(&ao_options, "dev", opt->ao_devicename);
}
static int
parse_options(shairplay_options_t *opt, int argc, char *argv[])
{
+ const char default_hwaddr[] = { 0x48, 0x5d, 0x60, 0x7c, 0xee, 0x22 };
+
char *path = argv[0];
char *arg;
- strcpy(opt->apname, "Shairplay");
+ /* Set default values for apname and port */
+ strncpy(opt->apname, "Shairplay", sizeof(opt->apname)-1);
opt->port = 5000;
+ memcpy(opt->hwaddr, default_hwaddr, sizeof(opt->hwaddr));
while ((arg = *++argv)) {
if (!strcmp(arg, "-a")) {
opt->port = atoi(*++argv);
} else if (!strncmp(arg, "--server_port=", 14)) {
opt->port = atoi(arg+14);
+ } else if (!strncmp(arg, "--hwaddr=", 9)) {
+ if (parse_hwaddr(arg+9, opt->hwaddr, sizeof(opt->hwaddr))) {
+ fprintf(stderr, "Invalid format given for hwaddr, aborting...\n");
+ fprintf(stderr, "Please use hwaddr format: 01:45:89:ab:cd:ef\n");
+ return 1;
+ }
} else if (!strncmp(arg, "--ao_driver=", 12)) {
strncpy(opt->ao_driver, arg+12, sizeof(opt->ao_driver)-1);
} else if (!strncmp(arg, "--ao_devicename=", 16)) {
fprintf(stderr, " -a, --apname=AirPort Sets Airport name\n");
fprintf(stderr, " -p, --password=secret Sets password\n");
fprintf(stderr, " -o, --server_port=5000 Sets port for RAOP service\n");
+ fprintf(stderr, " --hwaddr=address Sets the MAC address, useful if running multiple instances\n");
fprintf(stderr, " --ao_driver=driver Sets the ao driver (optional)\n");
fprintf(stderr, " --ao_devicename=devicename Sets the ao device name (optional)\n");
fprintf(stderr, " --ao_deviceid=id Sets the ao device id (optional)\n");
}
}
- /* Set default values for apname and port */
- if (!strlen(opt->apname)) {
- strncpy(opt->apname, "Shairplay", sizeof(opt->apname)-1);
- }
- if (!opt->port) {
- opt->port = 5000;
- }
return 0;
}
int
main(int argc, char *argv[])
{
- const char hwaddr[] = { 0x48, 0x5d, 0x60, 0x7c, 0xee, 0x22 };
-
shairplay_options_t options;
ao_device *device = NULL;
dnssd_t *dnssd;
raop_t *raop;
raop_callbacks_t raop_cbs;
+ char *password = NULL;
+
+ int error;
+
+#ifndef WIN32
+ init_signals();
+#endif
memset(&options, 0, sizeof(options));
if (parse_options(&options, argc, argv)) {
raop = raop_init_from_keyfile(10, &raop_cbs, "airport.key", NULL);
if (raop == NULL) {
fprintf(stderr, "Could not initialize the RAOP service\n");
+ fprintf(stderr, "Please make sure the airport.key file is in the current directory.\n");
return -1;
}
+ if (strlen(options.password)) {
+ password = options.password;
+ }
raop_set_log_level(raop, RAOP_LOG_DEBUG);
- raop_start(raop, &options.port, hwaddr, sizeof(hwaddr), NULL);
+ raop_start(raop, &options.port, options.hwaddr, sizeof(options.hwaddr), password);
+
+ error = 0;
+ dnssd = dnssd_init(&error);
+ if (error) {
+ fprintf(stderr, "ERROR: Could not initialize dnssd library!\n");
+ fprintf(stderr, "------------------------------------------\n");
+ fprintf(stderr, "You could try the following resolutions based on your OS:\n");
+ fprintf(stderr, "Windows: Try installing http://support.apple.com/kb/DL999\n");
+ fprintf(stderr, "Debian/Ubuntu: Try installing libavahi-compat-libdnssd-dev package\n");
+ raop_destroy(raop);
+ return -1;
+ }
- dnssd = dnssd_init(NULL);
- dnssd_register_raop(dnssd, options.apname, options.port, hwaddr, sizeof(hwaddr), 0);
+ dnssd_register_raop(dnssd, options.apname, options.port, options.hwaddr, sizeof(options.hwaddr), 0);
+ running = 1;
+ while (running) {
#ifndef WIN32
- sleep(100);
+ sleep(1);
#else
- Sleep(100*1000);
+ Sleep(1000);
#endif
+ }
dnssd_unregister_raop(dnssd);
dnssd_destroy(dnssd);