1 /*
2  * Starts the AirPlay server (name "FakePort") and dumps the received contents
3  * to files:
4  * - audio.pcm : decoded audio content
5  * - metadata.bin : meta data
6  * - covertart.jpg : cover art
7  *
8  * Requires avahi-daemon to run. Also requires file "airplay.key" in the same directory.
9  *
10  * Compile with: gcc -o example -g -I../../include/shairplay example.c -lshairplay
11  */
12 
13 #include <stdlib.h>
14 #include <stdio.h>
15 #include <unistd.h>
16 
17 #ifdef WIN32
18 #include <windows.h>
19 #endif
20 
21 #include "dnssd.h"
22 #include "raop.h"
23 
24 static int running;
25 
26 #ifndef WIN32
27 
28 #include <signal.h>
29 static void
signal_handler(int sig)30 signal_handler(int sig)
31 {
32 	switch (sig) {
33 	case SIGINT:
34 	case SIGTERM:
35 		running = 0;
36 		break;
37 	}
38 }
39 static void
init_signals(void)40 init_signals(void)
41 {
42 	struct sigaction sigact;
43 
44 	sigact.sa_handler = signal_handler;
45 	sigemptyset(&sigact.sa_mask);
46 	sigact.sa_flags = 0;
47 	sigaction(SIGINT, &sigact, NULL);
48 	sigaction(SIGTERM, &sigact, NULL);
49 }
50 
51 #endif
52 
53 static void *
audio_init(void * cls,int bits,int channels,int samplerate)54 audio_init(void *cls, int bits, int channels, int samplerate)
55 {
56 	return fopen("audio.pcm", "wb");
57 }
58 
59 static void
audio_set_volume(void * cls,void * session,float volume)60 audio_set_volume(void *cls, void *session, float volume)
61 {
62 	printf("Setting volume to %f\n", volume);
63 }
64 
65 static void
audio_set_metadata(void * cls,void * session,const void * buffer,int buflen)66 audio_set_metadata(void *cls, void *session, const void *buffer, int buflen)
67 {
68 	int orig = buflen;
69 	FILE *file = fopen("metadata.bin", "wb");
70 	while (buflen > 0) {
71 		buflen -= fwrite(buffer+orig-buflen, 1, buflen, file);
72 	}
73 	fclose(file);
74 	printf("Metadata of length %d saved as metadata.bin\n", orig);
75 }
76 
77 static void
audio_set_coverart(void * cls,void * session,const void * buffer,int buflen)78 audio_set_coverart(void *cls, void *session, const void *buffer, int buflen)
79 {
80 	int orig = buflen;
81 	FILE *file = fopen("coverart.jpg", "wb");
82 	while (buflen > 0) {
83 		buflen -= fwrite(buffer+orig-buflen, 1, buflen, file);
84 	}
85 	fclose(file);
86 	printf("Coverart of length %d saved as coverart.jpg\n", orig);
87 }
88 
89 static void
audio_process(void * cls,void * session,const void * buffer,int buflen)90 audio_process(void *cls, void *session, const void *buffer, int buflen)
91 {
92 	int orig = buflen;
93 	while (buflen > 0) {
94 		buflen -= fwrite(buffer+orig-buflen, 1, buflen, session);
95 	}
96 }
97 
98 static void
audio_flush(void * cls,void * session)99 audio_flush(void *cls, void *session)
100 {
101 	printf("Flushing audio\n");
102 }
103 
104 static void
audio_destroy(void * cls,void * session)105 audio_destroy(void *cls, void *session)
106 {
107 	fclose(session);
108 }
109 
110 static void
raop_log_callback(void * cls,int level,const char * msg)111 raop_log_callback(void *cls, int level, const char *msg)
112 {
113 	printf("RAOP LOG(%d): %s\n", level, msg);
114 }
115 
116 int
main(int argc,char * argv[])117 main(int argc, char *argv[])
118 {
119 	const char *name = "FakePort";
120 	unsigned short raop_port = 5000;
121 	const char hwaddr[] = { 0x48, 0x5d, 0x60, 0x7c, 0xee, 0x22 };
122 
123 	dnssd_t *dnssd;
124 	raop_t *raop;
125 	raop_callbacks_t raop_cbs;
126 
127 	int error;
128 
129 	raop_cbs.cls = NULL;
130 	raop_cbs.audio_init = audio_init;
131 	raop_cbs.audio_set_volume = audio_set_volume;
132 	raop_cbs.audio_set_metadata = audio_set_metadata;
133 	raop_cbs.audio_set_coverart = audio_set_coverart;
134 	raop_cbs.audio_process = audio_process;
135 	raop_cbs.audio_flush = audio_flush;
136 	raop_cbs.audio_destroy = audio_destroy;
137 
138 	raop = raop_init_from_keyfile(10, &raop_cbs, "airport.key", NULL);
139 	if (raop == NULL) {
140 		fprintf(stderr, "Could not initialize the RAOP service (airport.key missing?)\n");
141 		return -1;
142 	}
143 
144 	raop_set_log_level(raop, RAOP_LOG_DEBUG);
145 	raop_set_log_callback(raop, &raop_log_callback, NULL);
146 	raop_start(raop, &raop_port, hwaddr, sizeof(hwaddr), NULL);
147 
148 	error = 0;
149 	dnssd = dnssd_init(&error);
150 	if (error) {
151 		fprintf(stderr, "ERROR: Could not initialize dnssd library!\n");
152 		fprintf(stderr, "------------------------------------------\n");
153 		fprintf(stderr, "You could try the following resolutions based on your OS:\n");
154 		fprintf(stderr, "Windows: Try installing http://support.apple.com/kb/DL999\n");
155 		fprintf(stderr, "Debian/Ubuntu: Try installing libavahi-compat-libdnssd-dev package\n");
156 		raop_destroy(raop);
157 		return -1;
158 	}
159 
160 	dnssd_register_raop(dnssd, name, raop_port, hwaddr, sizeof(hwaddr), 1);
161 
162 	printf("Startup complete... Kill with Ctrl+C\n");
163 
164 	running = 1;
165 	while (running != 0) {
166 #ifndef WIN32
167 		sleep(1);
168 #else
169 		Sleep(1000);
170 #endif
171 	}
172 
173 	dnssd_unregister_raop(dnssd);
174 	dnssd_destroy(dnssd);
175 
176 	raop_stop(raop);
177 	raop_destroy(raop);
178 
179 	return 0;
180 }
181 
182