]> git.ipfire.org Git - thirdparty/shairport-sync.git/blob - dacp.c
Update check_classic_systemd_full.yml
[thirdparty/shairport-sync.git] / dacp.c
1 /*
2 * DACP protocol handler. This file is part of Shairport Sync.
3 * Copyright (c) Mike Brady 2017 -- 2020
4 * All rights reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person
7 * obtaining a copy of this software and associated documentation
8 * files (the "Software"), to deal in the Software without
9 * restriction, including without limitation the rights to use,
10 * copy, modify, merge, publish, distribute, sublicense, and/or
11 * sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 * OTHER DEALINGS IN THE SOFTWARE.
24 */
25
26 // Information about the four-character codes is from many sources, with thanks, including
27 // https://github.com/melloware/dacp-net/blob/master/Melloware.DACP/
28
29 #include "dacp.h"
30 #include "common.h"
31 #include "config.h"
32
33 #include <arpa/inet.h>
34 #include <errno.h>
35 #include <inttypes.h>
36 #include <math.h>
37 #include <memory.h>
38 #include <netdb.h>
39 #include <netinet/in.h>
40 #include <pthread.h>
41 #include <stdlib.h>
42 #include <sys/time.h>
43 #include <time.h>
44 #include <unistd.h>
45
46 #include "metadata_hub.h"
47 #include "tinyhttp/http.h"
48
49 typedef struct {
50 int players_connection_thread_index; // the connection thread index when a player thread is
51 // associated with this, zero otherwise
52 int scan_enable; // set to 1 if scanning should be considered
53 char dacp_id[256]; // the DACP ID string
54 uint16_t port; // zero if no port discovered
55 short connection_family; // AF_INET6 or AF_INET
56 int always_use_revision_number_1; // for dealing with forked-daapd;
57 uint32_t scope_id; // if it's an ipv6 connection, this will be its scope id
58 char ip_string[INET6_ADDRSTRLEN]; // the ip string pointing to the client
59 char *active_remote_id; // send this when you want to send remote control commands
60 void *port_monitor_private_storage;
61 } dacp_server_record;
62
63 int dacp_monitor_initialised = 0;
64 pthread_t dacp_monitor_thread;
65 dacp_server_record dacp_server;
66 void *mdns_dacp_monitor_private_storage_pointer;
67
68 // HTTP Response data/funcs (See the tinyhttp example.cpp file for more on this.)
69 struct HttpResponse {
70 void *body; // this will be a malloc'ed pointer
71 ssize_t malloced_size; // this will be its allocated size
72 ssize_t size; // the current size of the content
73 int code;
74 };
75
76 void *response_realloc(__attribute__((unused)) void *opaque, void *ptr, int size) {
77 void *t = realloc(ptr, size);
78 if ((t == NULL) && (size != 0))
79 debug(1, "Response realloc of size %d failed!", size);
80 return t;
81 }
82
83 void response_body(void *opaque, const char *data, int size) {
84 struct HttpResponse *response = (struct HttpResponse *)opaque;
85
86 ssize_t space_available = response->malloced_size - response->size;
87 if (space_available < size) {
88 // debug(1,"Getting more space for the response -- need %d bytes but only %ld bytes left.\n",
89 // size,
90 // size - space_available);
91 ssize_t size_requested = size - space_available + response->malloced_size + 16384;
92 void *t = realloc(response->body, size_requested);
93 response->malloced_size = size_requested;
94 if (t)
95 response->body = t;
96 else {
97 die("dacp: can't allocate any more space for parser.");
98 }
99 }
100 memcpy(response->body + response->size, data, size);
101 response->size += size;
102 }
103
104 static void
105 response_header(__attribute__((unused)) void *opaque, __attribute__((unused)) const char *ckey,
106 __attribute__((unused)) int nkey, __attribute__((unused)) const char *cvalue,
107 __attribute__((unused)) int nvalue) { /* example doesn't care about headers */
108 }
109
110 static void response_code(void *opaque, int code) {
111 struct HttpResponse *response = (struct HttpResponse *)opaque;
112 response->code = code;
113 }
114
115 static const struct http_funcs responseFuncs = {
116 response_realloc,
117 response_body,
118 response_header,
119 response_code,
120 };
121
122 // static pthread_mutex_t dacp_conversation_lock = PTHREAD_MUTEX_INITIALIZER;
123 // static pthread_mutex_t dacp_server_information_lock = PTHREAD_MUTEX_INITIALIZER;
124 static pthread_mutex_t dacp_conversation_lock;
125 static pthread_mutex_t dacp_server_information_lock;
126 static pthread_cond_t dacp_server_information_cv;
127
128 void addrinfo_cleanup(void *arg) {
129 // debug(1, "addrinfo cleanup called.");
130 struct addrinfo **info = (struct addrinfo **)arg;
131 freeaddrinfo(*info);
132 }
133
134 void mutex_lock_cleanup(void *arg) {
135 pthread_mutex_t *m = (pthread_mutex_t *)arg;
136 if (pthread_mutex_unlock(m))
137 debug(1, "Error releasing mutex.");
138 }
139
140 void connect_cleanup(void *arg) {
141 int *fd = (int *)arg;
142 // debug(2, "dacp_send_command: close socket %d.",*fd);
143 close(*fd);
144 }
145
146 void http_cleanup(void *arg) {
147 // debug(1, "http cleanup called.");
148 struct http_roundtripper *rt = (struct http_roundtripper *)arg;
149 http_free(rt);
150 }
151
152 int dacp_send_command(const char *command, char **body, ssize_t *bodysize) {
153 int result;
154 // debug(1,"dacp_send_command: command is: \"%s\".",command);
155
156 if (dacp_server.port == 0) {
157 // debug(3, "No DACP port specified yet");
158 result = 490; // no port specified
159 } else {
160
161 // will malloc space for the body or set it to NULL -- the caller should free it.
162
163 // Using some custom HTTP-like return codes
164 // 498 Bad Address information for the DACP server
165 // 497 Can't establish a socket to the DACP server
166 // 496 Can't connect to the DACP server
167 // 495 Error receiving response
168 // 494 This client is already busy
169 // 493 Client failed to send a message
170 // 492 Argument out of range
171 // 491 Client refused connection
172 // 490 No port specified
173
174 struct addrinfo hints, *res;
175 int sockfd;
176
177 struct HttpResponse response;
178 response.body = NULL;
179 response.malloced_size = 0;
180 response.size = 0;
181 response.code = 0;
182
183 char portstring[10], server[1024], message[1024];
184 memset(&portstring, 0, sizeof(portstring));
185 if (dacp_server.connection_family == AF_INET6) {
186 snprintf(server, sizeof(server), "%s%%%u", dacp_server.ip_string, dacp_server.scope_id);
187 } else {
188 strcpy(server, dacp_server.ip_string);
189 }
190 snprintf(portstring, sizeof(portstring), "%u", dacp_server.port);
191
192 // first, load up address structs with getaddrinfo():
193
194 memset(&hints, 0, sizeof(hints));
195 hints.ai_family = AF_UNSPEC;
196 hints.ai_socktype = SOCK_STREAM;
197
198 // debug(1, "DACP port string is \"%s:%s\".", server, portstring);
199
200 int ires = getaddrinfo(server, portstring, &hints, &res);
201 if (ires) {
202 // debug(1,"Error %d \"%s\" at getaddrinfo.",ires,gai_strerror(ires));
203 response.code = 498; // Bad Address information for the DACP server
204 } else {
205 uint64_t start_time = get_absolute_time_in_ns();
206 pthread_cleanup_push(addrinfo_cleanup, (void *)&res);
207 // only do this one at a time -- not sure it is necessary, but better safe than sorry
208
209 // int mutex_reply = sps_pthread_mutex_timedlock(&dacp_conversation_lock, 2000000, command,
210 // 1);
211 int mutex_reply = debug_mutex_lock(&dacp_conversation_lock, 2000000, 1);
212 // int mutex_reply = pthread_mutex_lock(&dacp_conversation_lock);
213 if (mutex_reply == 0) {
214 pthread_cleanup_push(mutex_lock_cleanup, (void *)&dacp_conversation_lock);
215
216 // make a socket:
217 sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
218
219 if (sockfd == -1) {
220 // debug(1, "DACP socket could not be created -- error %d:
221 // \"%s\".",errno,strerror(errno));
222 response.code = 497; // Can't establish a socket to the DACP server
223 } else {
224 pthread_cleanup_push(connect_cleanup, (void *)&sockfd);
225 // debug(2, "dacp_send_command: open socket %d.",sockfd);
226
227 // This is for limiting the time to be spent waiting for a response.
228
229 struct timeval tv;
230 tv.tv_sec = 0;
231 tv.tv_usec = 500000;
232 if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof tv) == -1)
233 debug(1, "dacp_send_command: error %d setting receive timeout.", errno);
234 if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, (const char *)&tv, sizeof tv) == -1)
235 debug(1, "dacp_send_command: error %d setting send timeout.", errno);
236
237 // connect!
238 // debug(1, "DACP socket created.");
239 if (connect(sockfd, res->ai_addr, res->ai_addrlen) < 0) {
240 // debug(1, "dacp_send_command: connect failed with errno %d.", errno);
241 if (errno == ECONNREFUSED)
242 response.code = 491; // DACP server doesn't want to talk anymore...
243 else
244 response.code = 496; // Can't connect to the DACP server
245 } else {
246 // debug(1,"DACP connect succeeded.");
247
248 snprintf(message, sizeof(message),
249 "GET /ctrl-int/1/%s HTTP/1.1\r\nHost: %s:%u\r\nActive-Remote: %s\r\n\r\n",
250 command, dacp_server.ip_string, dacp_server.port,
251 dacp_server.active_remote_id);
252
253 // Send command
254 debug(3, "dacp_send_command: \"%s\".", command);
255 ssize_t wresp = send(sockfd, message, strlen(message), 0);
256 if (wresp == -1) {
257 char errorstring[1024];
258 strerror_r(errno, (char *)errorstring, sizeof(errorstring));
259 debug(2, "dacp_send_command: write error %d: \"%s\".", errno, (char *)errorstring);
260 struct linger so_linger;
261 so_linger.l_onoff = 1; // "true"
262 so_linger.l_linger = 0;
263 int err = setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &so_linger, sizeof so_linger);
264 if (err)
265 debug(1, "Could not set the dacp socket to abort due to a write error on closing.");
266 }
267 if (wresp != (ssize_t)strlen(message)) {
268 // debug(1, "dacp_send_command: send failed.");
269 response.code = 493; // Client failed to send a message
270
271 } else {
272
273 response.body = malloc(2048); // it can resize this if necessary
274 response.malloced_size = 2048;
275 pthread_cleanup_push(malloc_cleanup, response.body);
276
277 struct http_roundtripper rt;
278 http_init(&rt, responseFuncs, &response);
279 pthread_cleanup_push(http_cleanup, &rt);
280
281 int needmore = 1;
282 int looperror = 0;
283 char buffer[8192];
284 memset(buffer, 0, sizeof(buffer));
285 while (needmore && !looperror) {
286 const char *data = buffer;
287 if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof tv) == -1)
288 debug(1, "dacp_send_command: error %d setting receive timeout.", errno);
289 ssize_t ndata = recv(sockfd, buffer, sizeof(buffer), 0);
290 // debug(3, "Received %d bytes: \"%s\".", ndata, buffer);
291 if (ndata <= 0) {
292 if (ndata == -1) {
293 char errorstring[1024];
294 strerror_r(errno, (char *)errorstring, sizeof(errorstring));
295 debug(2, "dacp_send_command: receiving error %d: \"%s\".", errno,
296 (char *)errorstring);
297 struct linger so_linger;
298 so_linger.l_onoff = 1; // "true"
299 so_linger.l_linger = 0;
300 int err =
301 setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &so_linger, sizeof so_linger);
302 if (err)
303 debug(
304 1,
305 "Could not set the dacp socket to abort due to a read error on closing.");
306 }
307
308 free(response.body);
309 response.body = NULL;
310 response.malloced_size = 0;
311 response.size = 0;
312 response.code = 495; // Error receiving response
313 looperror = 1;
314 }
315
316 while (needmore && ndata && !looperror) {
317 int read;
318 needmore = http_data(&rt, data, ndata, &read);
319 ndata -= read;
320 data += read;
321 }
322 }
323
324 if (http_iserror(&rt)) {
325 debug(3, "dacp_send_command: error parsing data.");
326 free(response.body);
327 response.body = NULL;
328 response.malloced_size = 0;
329 response.size = 0;
330 }
331 // debug(1,"Size of response body is %d",response.size);
332 pthread_cleanup_pop(1); // this should call http_cleanup
333 // http_free(&rt);
334 pthread_cleanup_pop(
335 0); // this should *not* free the malloced buffer -- just pop the malloc cleanup
336 }
337 }
338 pthread_cleanup_pop(1); // this should close the socket
339 // close(sockfd);
340 // debug(1,"DACP socket closed.");
341 }
342 pthread_cleanup_pop(1); // this should unlock the dacp_conversation_lock);
343 // pthread_mutex_unlock(&dacp_conversation_lock);
344 // debug(1,"Sent command\"%s\" with a response body of size %d.",command,response.size);
345 // debug(1,"dacp_conversation_lock released.");
346 } else {
347 debug(3,
348 "dacp_send_command: could not acquire a lock on the dacp transmit/receive section "
349 "when attempting to "
350 "send the command \"%s\". Possible timeout?",
351 command);
352 response.code = 494; // This client is already busy
353 }
354 pthread_cleanup_pop(1); // this should free the addrinfo
355 // freeaddrinfo(res);
356 uint64_t et = get_absolute_time_in_ns() - start_time; // this will be in nanoseconds
357 debug(3, "dacp_send_command: %f seconds, response code %d, command \"%s\".",
358 (1.0 * et) / 1000000000, response.code, command);
359 }
360 *body = response.body;
361 *bodysize = response.size;
362 result = response.code;
363 }
364 return result;
365 }
366
367 int send_simple_dacp_command(const char *command) {
368 int reply = 0;
369 char *server_reply = NULL;
370 debug(2, "send_simple_dacp_command: sending command \"%s\".", command);
371 ssize_t reply_size = 0;
372 reply = dacp_send_command(command, &server_reply, &reply_size);
373 if (server_reply) {
374 free(server_reply);
375 server_reply = NULL;
376 }
377 return reply;
378 }
379
380 void relinquish_dacp_server_information(rtsp_conn_info *conn) {
381 // this will set the dacp_server.players_connection_thread_index to zero iff it has the same value
382 // as the conn's connection number
383 // this is to signify that the player has stopped, but only if another thread (with a different
384 // index) hasn't already taken over the dacp service
385 debug_mutex_lock(&dacp_server_information_lock, 500000, 2);
386 if (dacp_server.players_connection_thread_index == conn->connection_number)
387 dacp_server.players_connection_thread_index = 0;
388 debug_mutex_unlock(&dacp_server_information_lock, 3);
389 }
390
391 // this will be running on the thread of its caller, not of the conversation thread...
392 // tell the DACP conversation thread what DACP server we are listening to
393 // if the active_remote_id is the same we don't change anything apart from
394 // the conversation number
395 // Thus, we can keep the DACP port that might have previously been discovered
396 void set_dacp_server_information(rtsp_conn_info *conn) {
397 // debug(1, "set_dacp_server_information");
398 debug_mutex_lock(&dacp_server_information_lock, 500000, 2);
399 dacp_server.players_connection_thread_index = conn->connection_number;
400
401 if ((conn->dacp_id == NULL) || (strcmp(conn->dacp_id, dacp_server.dacp_id) != 0)) {
402 if (conn->dacp_id)
403 strncpy(dacp_server.dacp_id, conn->dacp_id, sizeof(dacp_server.dacp_id) - 1);
404 else
405 dacp_server.dacp_id[0] = '\0';
406 dacp_server.port = 0;
407 dacp_server.scan_enable = 0;
408 dacp_server.connection_family = conn->connection_ip_family;
409 dacp_server.scope_id = conn->self_scope_id;
410 strncpy(dacp_server.ip_string, conn->client_ip_string, INET6_ADDRSTRLEN);
411 debug(2, "set_dacp_server_information set IP to \"%s\" and DACP id to \"%s\".",
412 dacp_server.ip_string, dacp_server.dacp_id);
413
414 /*
415
416 "long polling" is not implemented by Shairport Sync, whereby by sending the client the
417 last-received revision number, the link will hang until a change occurs.
418
419 Instead, at present, Shairport Sync always uses a revision number of 1.
420
421 */
422
423 // always use revision number 1
424 dacp_server.always_use_revision_number_1 = 1;
425
426 metadata_hub_modify_prolog();
427 int ch = metadata_store.dacp_server_active != dacp_server.scan_enable;
428 metadata_store.dacp_server_active = dacp_server.scan_enable;
429 if ((metadata_store.client_ip == NULL) ||
430 (strncmp(metadata_store.client_ip, conn->client_ip_string, INET6_ADDRSTRLEN) != 0)) {
431 if (metadata_store.client_ip)
432 free(metadata_store.client_ip);
433 metadata_store.client_ip = strndup(conn->client_ip_string, INET6_ADDRSTRLEN);
434 debug(3, "MH Client IP set to: \"%s\"", metadata_store.client_ip);
435 ch = 1;
436 }
437 metadata_hub_modify_epilog(ch);
438 } else {
439 if (dacp_server.port) {
440 // debug(1, "Re-enable scanning.");
441 dacp_server.scan_enable = 1;
442 // metadata_hub_modify_prolog();
443 // int ch = metadata_store.dacp_server_active != dacp_server.scan_enable;
444 // metadata_store.dacp_server_active = dacp_server.scan_enable;
445 // metadata_hub_modify_epilog(ch);
446 }
447 }
448 if (dacp_server.active_remote_id)
449 free(dacp_server.active_remote_id);
450 if (conn->dacp_active_remote)
451 dacp_server.active_remote_id =
452 strdup(conn->dacp_active_remote); // even if the dacp_id remains the same,
453 // the active remote will change.
454 else
455 dacp_server.active_remote_id = NULL;
456
457 debug(3, "set_dacp_server_information set active-remote id to %s.", dacp_server.active_remote_id);
458 pthread_cond_signal(&dacp_server_information_cv);
459 debug_mutex_unlock(&dacp_server_information_lock, 3);
460 }
461
462 void dacp_monitor_port_update_callback(char *dacp_id, uint16_t port) {
463 debug_mutex_lock(&dacp_server_information_lock, 500000, 2);
464 debug(2,
465 "dacp_monitor_port_update_callback with Remote ID \"%s\", target ID \"%s\" and port "
466 "number %d.",
467 dacp_id, dacp_server.dacp_id, port);
468 if ((dacp_id == NULL) || (dacp_server.dacp_id[0] == '\0')) {
469 warn("dacp_id or dacp_server.dacp_id NULL detected");
470 } else {
471 if (strcmp(dacp_id, dacp_server.dacp_id) == 0) {
472 dacp_server.port = port;
473 if (port == 0)
474 dacp_server.scan_enable = 0;
475 else {
476 dacp_server.scan_enable = 1;
477 // debug(2, "dacp_monitor_port_update_callback enables scan");
478 }
479 // metadata_hub_modify_prolog();
480 // int ch = metadata_store.dacp_server_active != dacp_server.scan_enable;
481 // metadata_store.dacp_server_active = dacp_server.scan_enable;
482 // metadata_hub_modify_epilog(ch);
483 } else {
484 debug(2, "dacp port monitor reporting on an out-of-use remote.");
485 }
486 }
487 pthread_cond_signal(&dacp_server_information_cv);
488 debug_mutex_unlock(&dacp_server_information_lock, 3);
489 }
490
491 void *dacp_monitor_thread_code(__attribute__((unused)) void *na) {
492 int scan_index = 0;
493 int always_use_revision_number_1 = 0;
494 // char server_reply[10000];
495 // debug(1, "DACP monitor thread started.");
496 // wait until we get a valid port number to begin monitoring it
497 int32_t revision_number = 1;
498 int bad_result_count = 0;
499 int idle_scan_count = 0;
500 while (1) {
501 int result = 0;
502 int32_t the_volume;
503 pthread_cleanup_debug_mutex_lock(&dacp_server_information_lock, 500000, 2);
504 if (dacp_server.scan_enable == 0) {
505 metadata_hub_modify_prolog();
506 int ch = (metadata_store.dacp_server_active != 0) ||
507 (metadata_store.advanced_dacp_server_active != 0);
508 metadata_store.dacp_server_active = 0;
509 metadata_store.advanced_dacp_server_active = 0;
510 /*
511 debug(3,
512 "setting metadata_store.dacp_server_active and "
513 "metadata_store.advanced_dacp_server_active to 0 with an update "
514 "flag value of %d",
515 ch);
516 */
517 metadata_hub_modify_epilog(ch);
518
519 uint64_t time_to_wait_for_wakeup_ns =
520 (uint64_t)(1000000000 * config.missing_port_dacp_scan_interval_seconds);
521
522 #ifdef COMPILE_FOR_LINUX_AND_FREEBSD_AND_CYGWIN_AND_OPENBSD
523 uint64_t time_of_wakeup_ns = get_realtime_in_ns() + time_to_wait_for_wakeup_ns;
524 uint64_t sec = time_of_wakeup_ns / 1000000000;
525 uint64_t nsec = time_of_wakeup_ns % 1000000000;
526 #endif
527 #ifdef COMPILE_FOR_OSX
528 uint64_t sec = time_to_wait_for_wakeup_ns / 1000000000;
529 uint64_t nsec = time_to_wait_for_wakeup_ns % 1000000000;
530 #endif
531
532 struct timespec time_to_wait;
533 time_to_wait.tv_sec = sec;
534 time_to_wait.tv_nsec = nsec;
535
536 while ((dacp_server.scan_enable == 0) && (result != ETIMEDOUT)) {
537 // debug(1, "dacp_monitor_thread_code wait for an event to possibly enable scan");
538
539 #ifdef COMPILE_FOR_LINUX_AND_FREEBSD_AND_CYGWIN_AND_OPENBSD
540 result = pthread_cond_timedwait(&dacp_server_information_cv, &dacp_server_information_lock,
541 &time_to_wait); // this is a pthread cancellation point
542 // debug(1, "result is %d, dacp_server.scan_enable is %d, time_to_wait_for_wakeup_ns is %"
543 // PRId64 ".", result, dacp_server.scan_enable, time_to_wait_for_wakeup_ns);
544
545 #endif
546 #ifdef COMPILE_FOR_OSX
547 result = pthread_cond_timedwait_relative_np(&dacp_server_information_cv,
548 &dacp_server_information_lock, &time_to_wait);
549 #endif
550 }
551 if (dacp_server.scan_enable == 1) {
552 bad_result_count = 0;
553 idle_scan_count = 0;
554 }
555 }
556
557 always_use_revision_number_1 =
558 dacp_server.always_use_revision_number_1; // set this while access is locked
559
560 result = dacp_get_volume(&the_volume); // just want the http code
561 pthread_cleanup_pop(1);
562
563 if (result == 490) { // 490 means no port was specified
564 if (strlen(dacp_server.dacp_id) != 0) {
565 // debug(1,"mdns_dacp_monitor_set_id");
566 mdns_dacp_monitor_set_id(dacp_server.dacp_id);
567 }
568 } else {
569 scan_index++;
570 // debug(1,"DACP Scan Result: %d.", result);
571
572 if ((result == 200) || (result == 400)) {
573 bad_result_count = 0;
574 } else {
575 if (bad_result_count < config.scan_max_bad_response_count) // limit to some reasonable value
576 bad_result_count++;
577 }
578
579 // here, do the debouncing calculations to see
580 // if the dacp server and the
581 // advanced dacp server are available
582
583 // -1 means we don't know because some bad statuses have been reported
584 // 0 means definitely no
585 // +1 means definitely yes
586
587 int dacp_server_status_now = -1;
588 int advanced_dacp_server_status_now = -1;
589
590 if (bad_result_count == 0) {
591 dacp_server_status_now = 1;
592 if (result == 200)
593 advanced_dacp_server_status_now = 1;
594 else if (result == 400)
595 advanced_dacp_server_status_now = 0;
596 } else if (bad_result_count ==
597 config.scan_max_bad_response_count) { // if a sequence of bad return codes occurs,
598 // then it's gone
599 dacp_server_status_now = 0;
600 advanced_dacp_server_status_now = 0;
601 }
602
603 if (metadata_store.player_thread_active == 0)
604 idle_scan_count++;
605 else
606 idle_scan_count = 0;
607
608 debug(3, "Scan Result: %d, Bad Scan Count: %d, Idle Scan Count: %d.", result,
609 bad_result_count, idle_scan_count);
610
611 /* not used
612 // decide if idle for too long
613 if (idle_scan_count == config.scan_max_inactive_count) {
614 debug(2, "DACP server status scanning stopped.");
615 dacp_server.scan_enable = 0;
616 }
617 */
618
619 int update_needed = 0;
620 metadata_hub_modify_prolog();
621 if (dacp_server_status_now != -1) { // if dacp_server_status_now is actually known...
622 if (metadata_store.dacp_server_active != dacp_server_status_now) {
623 debug(2, "metadata_store.dacp_server_active set to %d.", dacp_server_status_now);
624 metadata_store.dacp_server_active = dacp_server_status_now;
625 update_needed = 1;
626 }
627 }
628 if (advanced_dacp_server_status_now !=
629 -1) { // if advanced_dacp_server_status_now is actually known...
630 if (metadata_store.advanced_dacp_server_active != advanced_dacp_server_status_now) {
631 debug(2, "metadata_store.advanced_dacp_server_active set to %d.", dacp_server_status_now);
632 metadata_store.advanced_dacp_server_active = advanced_dacp_server_status_now;
633 update_needed = 1;
634 }
635 }
636
637 metadata_hub_modify_epilog(update_needed);
638
639 // pthread_mutex_unlock(&dacp_server_information_lock);
640 // debug(1, "DACP Server ID \"%u\" at \"%s:%u\", scan %d.", dacp_server.active_remote_id,
641 // dacp_server.ip_string, dacp_server.port, scan_index);
642
643 if (result == 200) {
644 metadata_hub_modify_prolog();
645 int diff = metadata_store.speaker_volume != the_volume;
646 if (diff)
647 metadata_store.speaker_volume = the_volume;
648 metadata_hub_modify_epilog(diff);
649
650 ssize_t le;
651 char *response = NULL;
652 int32_t item_size;
653 char command[1024] = "";
654 if (always_use_revision_number_1 != 0) // see the "long polling" note above
655 revision_number = 1;
656 snprintf(command, sizeof(command) - 1, "playstatusupdate?revision-number=%d",
657 revision_number);
658 // debug(1,"dacp_monitor_thread_code: command: \"%s\"",command);
659 result = dacp_send_command(command, &response, &le);
660 // debug(1,"Response to \"%s\" is %d.",command,result);
661 // remember: unless the revision_number you pass in is 1,
662 // response will be 200 only if there's something new to report.
663 if (result == 200) {
664 // if (0) {
665 char *sp = response;
666 if (le >= 8) {
667 // here start looking for the contents of the status update
668 if (dacp_tlv_crawl(&sp, &item_size) == 'cmst') { // status
669 // here, we know that we are receiving playerstatusupdates, so set a flag
670 metadata_hub_modify_prolog();
671 // debug(1, "playstatusupdate release track metadata");
672 // metadata_hub_reset_track_metadata();
673 // metadata_store.playerstatusupdates_are_received = 1;
674 sp -= item_size; // drop down into the array -- don't skip over it
675 le -= 8;
676 // char typestring[5];
677 // we need to acquire the metadata data structure and possibly update it
678 while (le >= 8) {
679 uint32_t type = dacp_tlv_crawl(&sp, &item_size);
680 le -= item_size + 8;
681 char *t;
682 // char u;
683 // char *st;
684 int32_t r;
685 uint32_t ui;
686 // uint64_t v;
687 // int i;
688
689 switch (type) {
690 case 'cmsr': // revision number
691 t = sp - item_size;
692 revision_number = ntohl(*(uint32_t *)(t));
693 // debug(1,"New revision number received: %d", revision_number);
694 break;
695 case 'caps': // play status
696 t = sp - item_size;
697 r = *(unsigned char *)(t);
698 switch (r) {
699 case 2:
700 if (metadata_store.play_status != PS_STOPPED) {
701 metadata_store.play_status = PS_STOPPED;
702 debug(2, "Play status is \"stopped\".");
703 }
704 break;
705 case 3:
706 if (metadata_store.play_status != PS_PAUSED) {
707 metadata_store.play_status = PS_PAUSED;
708 debug(2, "Play status is \"paused\".");
709 }
710 break;
711 case 4:
712 if (metadata_store.play_status != PS_PLAYING) {
713 metadata_store.play_status = PS_PLAYING;
714 debug(2, "Play status changed to \"playing\".");
715 }
716 break;
717 default:
718 debug(1, "Unrecognised play status %d received.", r);
719 break;
720 }
721 break;
722 case 'cash': // shuffle status
723 t = sp - item_size;
724 r = *(unsigned char *)(t);
725 switch (r) {
726 case 0:
727 if (metadata_store.shuffle_status != SS_OFF) {
728 metadata_store.shuffle_status = SS_OFF;
729 debug(2, "Shuffle status is \"off\".");
730 }
731 break;
732 case 1:
733 if (metadata_store.shuffle_status != SS_ON) {
734 metadata_store.shuffle_status = SS_ON;
735 debug(2, "Shuffle status is \"on\".");
736 }
737 break;
738 default:
739 debug(1, "Unrecognised shuffle status %d received.", r);
740 break;
741 }
742 break;
743 case 'carp': // repeat status
744 t = sp - item_size;
745 r = *(unsigned char *)(t);
746 switch (r) {
747 case 0:
748 if (metadata_store.repeat_status != RS_OFF) {
749 metadata_store.repeat_status = RS_OFF;
750 debug(2, "Repeat status is \"none\".");
751 }
752 break;
753 case 1:
754 if (metadata_store.repeat_status != RS_ONE) {
755 metadata_store.repeat_status = RS_ONE;
756 debug(2, "Repeat status is \"one\".");
757 }
758 break;
759 case 2:
760 if (metadata_store.repeat_status != RS_ALL) {
761 metadata_store.repeat_status = RS_ALL;
762 debug(2, "Repeat status is \"all\".");
763 }
764 break;
765 default:
766 debug(1, "Unrecognised repeat status %d received.", r);
767 break;
768 }
769 break;
770 case 'cann': // track name
771 debug(2, "DACP Track Name seen");
772 if (string_update_with_size(&metadata_store.track_name,
773 &metadata_store.track_name_changed, sp - item_size,
774 item_size)) {
775 debug(2, "DACP Track Name set to: \"%s\"", metadata_store.track_name);
776 }
777 break;
778 case 'cana': // artist name
779 debug(2, "DACP Artist Name seen");
780 if (string_update_with_size(&metadata_store.artist_name,
781 &metadata_store.artist_name_changed, sp - item_size,
782 item_size)) {
783 debug(2, "DACP Artist Name set to: \"%s\"", metadata_store.artist_name);
784 }
785 break;
786 case 'canl': // album name
787 debug(2, "DACP Album Name seen");
788 if (string_update_with_size(&metadata_store.album_name,
789 &metadata_store.album_name_changed, sp - item_size,
790 item_size)) {
791 debug(2, "DACP Album Name set to: \"%s\"", metadata_store.album_name);
792 }
793 break;
794 case 'cang': // genre
795 debug(2, "DACP Genre seen");
796 if (string_update_with_size(&metadata_store.genre, &metadata_store.genre_changed,
797 sp - item_size, item_size)) {
798 debug(2, "DACP Genre set to: \"%s\"", metadata_store.genre);
799 }
800 break;
801 case 'canp': // nowplaying 4 ids: dbid, plid, playlistItem, itemid (from mellowware
802 // see reference above)
803 debug(2, "DACP Composite ID seen");
804 if ((metadata_store.item_composite_id_is_valid == 0) ||
805 (memcmp(metadata_store.item_composite_id, sp - item_size,
806 sizeof(metadata_store.item_composite_id)) != 0)) {
807 memcpy(metadata_store.item_composite_id, sp - item_size,
808 sizeof(metadata_store.item_composite_id));
809 char st[33];
810 char *pt = st;
811 int it;
812 for (it = 0; it < 16; it++) {
813 snprintf(pt, 3, "%02X", metadata_store.item_composite_id[it]);
814 pt += 2;
815 }
816 *pt = 0;
817 debug(2, "Item composite ID changed to 0x%s.", st);
818 metadata_store.item_composite_id_changed = 1;
819 metadata_store.item_composite_id_is_valid = 1;
820 }
821 break;
822 case 'astm':
823 t = sp - item_size;
824 ui = ntohl(*(uint32_t *)(t));
825 debug(2, "DACP Song Time seen: \"%u\" of length %u.", ui, item_size);
826 if (ui != metadata_store.songtime_in_milliseconds) {
827 metadata_store.songtime_in_milliseconds = ui;
828 metadata_store.songtime_in_milliseconds_changed = 1;
829 metadata_store.songtime_in_milliseconds_is_valid = 1;
830 debug(2, "DACP Song Time set to: \"%u\"",
831 metadata_store.songtime_in_milliseconds);
832 }
833 break;
834
835 /*
836 case 'mstt':
837 case 'cant':
838 case 'cast':
839 case 'cmmk':
840 case 'caas':
841 case 'caar':
842 t = sp - item_size;
843 r = ntohl(*(uint32_t *)(t));
844 printf(" %d", r);
845 printf(" (0x");
846 t = sp - item_size;
847 for (i = 0; i < item_size; i++) {
848 printf("%02x", *t & 0xff);
849 t++;
850 }
851 printf(")");
852 break;
853 case 'asai':
854 t = sp - item_size;
855 s = ntohl(*(uint32_t *)(t));
856 s = s << 32;
857 t += 4;
858 v = (ntohl(*(uint32_t *)(t))) & 0xffffffff;
859 s += v;
860 printf(" %lu", s);
861 printf(" (0x");
862 t = sp - item_size;
863 for (i = 0; i < item_size; i++) {
864 printf("%02x", *t & 0xff);
865 t++;
866 }
867 printf(")");
868 break;
869 */
870 default:
871 /*
872 printf(" 0x");
873 t = sp - item_size;
874 for (i = 0; i < item_size; i++) {
875 printf("%02x", *t & 0xff);
876 t++;
877 }
878 */
879 break;
880 }
881 // printf("\n");
882 }
883
884 // finished possibly writing to the metadata hub
885 metadata_hub_modify_epilog(
886 1); // should really see if this can be made responsive to changes
887 } else {
888 debug(1, "Status Update not found.\n");
889 }
890 } else {
891 debug(1, "Can't find any content in playerstatusupdate request");
892 }
893 } /* else {
894 if (result != 403)
895 debug(1, "Unexpected response %d to playerstatusupdate request", result);
896 } */
897 if (response) {
898 free(response);
899 response = NULL;
900 };
901 };
902 /*
903 strcpy(command,"nowplayingartwork?mw=320&mh=320");
904 debug(1,"Command: \"%s\", result is %d",command, dacp_send_command(command, &response, &le));
905 if (response) {
906 free(response);
907 response = NULL;
908 }
909 strcpy(command,"getproperty?properties=dmcp.volume");
910 debug(1,"Command: \"%s\", result is %d",command, dacp_send_command(command, &response, &le));
911 if (response) {
912 free(response);
913 response = NULL;
914 }
915 strcpy(command,"setproperty?dmcp.volume=100.000000");
916 debug(1,"Command: \"%s\", result is %d",command, dacp_send_command(command, &response, &le));
917 if (response) {
918 free(response);
919 response = NULL;
920 }
921 */
922 if (metadata_store.player_thread_active)
923 sleep(config.scan_interval_when_active);
924 else
925 sleep(config.scan_interval_when_inactive);
926 }
927 }
928 debug(1, "DACP monitor thread exiting -- should never happen.");
929 pthread_exit(NULL);
930 }
931
932 void dacp_monitor_start() {
933 int rc;
934
935 rc = pthread_cond_init(&dacp_server_information_cv, NULL);
936 if (rc)
937 debug(1, "Error initialising the DACP Server Information Condition Variable");
938
939 pthread_mutexattr_t mta;
940
941 rc = pthread_mutexattr_init(&mta);
942 if (rc)
943 debug(1, "Error creating the DACP Conversation Lock Mutex Att Init");
944
945 rc = pthread_mutexattr_settype(&mta, PTHREAD_MUTEX_ERRORCHECK);
946 if (rc)
947 debug(1, "Error creating the DACP Conversation Lock Mutex Errorcheck");
948
949 // rc = pthread_mutexattr_setname_np(&mta, "DACP Conversation Lock");
950 // if (rc)
951 // debug(1,"Error creating the DACP Conversation Lock Mutex Set Name");
952
953 rc = pthread_mutex_init(&dacp_conversation_lock, &mta);
954 if (rc)
955 debug(1, "Error creating the DACP Conversation Lock Mutex Init");
956 // else
957 // debug(1, "DACP Conversation Lock Mutex Init");
958
959 rc = pthread_mutexattr_destroy(&mta);
960 if (rc)
961 debug(1, "Error creating the DACP Conversation Lock Attr Destroy");
962
963 rc = pthread_mutexattr_init(&mta);
964 if (rc)
965 debug(1, "Error creating the DACP Server Information Lock Mutex Att Init");
966
967 rc = pthread_mutexattr_settype(&mta, PTHREAD_MUTEX_ERRORCHECK);
968 if (rc)
969 debug(1, "Error creating the DACP Server Information Lock Mutex Errorcheck");
970
971 // rc = pthread_mutexattr_setname_np(&mta, "DACP Conversation Lock");
972 // if (rc)
973 // debug(1,"Error creating the DACP Server Information Lock Mutex Set Name");
974
975 rc = pthread_mutex_init(&dacp_server_information_lock, &mta);
976 if (rc)
977 debug(1, "Error creating the DACP Server Information Lock Mutex Init");
978
979 rc = pthread_mutexattr_destroy(&mta);
980 if (rc)
981 debug(1, "Error creating the DACP Server Information Lock Attr Destroy");
982
983 memset(&dacp_server, 0, sizeof(dacp_server_record));
984
985 pthread_create(&dacp_monitor_thread, NULL, dacp_monitor_thread_code, NULL);
986 dacp_monitor_initialised = 1;
987 }
988
989 void dacp_monitor_stop() {
990 if (dacp_monitor_initialised) { // only if it's been started and initialised
991 debug(2, "dacp_monitor_stop");
992 pthread_cancel(dacp_monitor_thread);
993 pthread_join(dacp_monitor_thread, NULL);
994 pthread_mutex_destroy(&dacp_server_information_lock);
995 debug(3, "DACP Conversation Lock Mutex Destroyed");
996 pthread_mutex_destroy(&dacp_conversation_lock);
997 pthread_cond_destroy(&dacp_server_information_cv);
998 debug(3, "DACP Server Information Condition Variable destroyed.");
999 if (dacp_server.active_remote_id) {
1000 free(dacp_server.active_remote_id);
1001 dacp_server.active_remote_id = NULL;
1002 }
1003 }
1004 }
1005
1006 uint32_t dacp_tlv_crawl(char **p, int32_t *length) {
1007 char typecode[5];
1008 memcpy(typecode, *p, 4);
1009 typecode[4] = '\0';
1010 uint32_t type = ntohl(*(uint32_t *)*p);
1011 *p += 4;
1012 *length = ntohl(*(uint32_t *)*p);
1013 *p += 4 + *length;
1014 // debug(1,"Type seen: '%s' of length %d",typecode,*length);
1015 return type;
1016 }
1017
1018 int dacp_get_client_volume(int32_t *result) {
1019 // debug(1,"dacp_get_client_volume");
1020 char *server_reply = NULL;
1021 int32_t overall_volume = -1;
1022 ssize_t reply_size;
1023 // debug(1,"dacp_get_client_volume: dacp_send_command");
1024 int response =
1025 dacp_send_command("getproperty?properties=dmcp.volume", &server_reply, &reply_size);
1026 if (response == 200) { // if we get an okay
1027 char *sp = server_reply;
1028 int32_t item_size;
1029 if (reply_size >= 8) {
1030 if (dacp_tlv_crawl(&sp, &item_size) == 'cmgt') {
1031 sp -= item_size; // drop down into the array -- don't skip over it
1032 reply_size -= 8;
1033 while (reply_size >= 8) {
1034 uint32_t type = dacp_tlv_crawl(&sp, &item_size);
1035 reply_size -= item_size + 8;
1036 if (type == 'cmvo') { // drop down into the dictionary -- don't skip over it
1037 char *t = sp - item_size;
1038 overall_volume = ntohl(*(uint32_t *)(t));
1039 }
1040 }
1041 } else {
1042 debug(1, "Unexpected payload response from getproperty?properties=dmcp.volume");
1043 }
1044 } else {
1045 debug(1, "Too short a response from getproperty?properties=dmcp.volume");
1046 }
1047 // debug(1, "Overall Volume is %d.", overall_volume);
1048 }
1049
1050 if (server_reply) {
1051 // debug(1, "Freeing response memory.");
1052 free(server_reply);
1053 server_reply = NULL;
1054 }
1055
1056 if (result) {
1057 *result = overall_volume;
1058 // debug(1,"dacp_get_client_volume returns: %" PRId32 ".",overall_volume);
1059 }
1060 return response;
1061 }
1062
1063 int dacp_set_include_speaker_volume(int64_t machine_number, int32_t vo) {
1064 debug(2, "dacp_set_include_speaker_volume to %" PRId32 ".", vo);
1065 char message[1000];
1066 memset(message, 0, sizeof(message));
1067 snprintf(message, sizeof(message),
1068 "setproperty?include-speaker-id=%" PRId64 "&dmcp.volume=%" PRId32 "", machine_number,
1069 vo);
1070 debug(2, "sending \"%s\"", message);
1071 return send_simple_dacp_command(message);
1072 // should return 204
1073 }
1074
1075 int dacp_set_speaker_volume(int64_t machine_number, int32_t vo) {
1076 char message[1000];
1077 memset(message, 0, sizeof(message));
1078 snprintf(message, sizeof(message), "setproperty?speaker-id=%" PRId64 "&dmcp.volume=%" PRId32 "",
1079 machine_number, vo);
1080 debug(2, "sending \"%s\"", message);
1081 return send_simple_dacp_command(message);
1082 // should return 204
1083 }
1084
1085 int dacp_get_speaker_list(dacp_spkr_stuff *speaker_info, int max_size_of_array,
1086 int *actual_speaker_count) {
1087 // char typestring[5];
1088 char *server_reply = NULL;
1089 int speaker_index = -1; // will be incremented before use
1090 int speaker_count = -1; // will be fixed if there is no problem
1091 ssize_t le;
1092
1093 // debug(1,"dacp_speaker_list: dacp_send_command");
1094 int response = dacp_send_command("getspeakers", &server_reply, &le);
1095 if (response == 200) {
1096 char *sp = server_reply;
1097 int32_t item_size;
1098 if (le >= 8) {
1099 if (dacp_tlv_crawl(&sp, &item_size) == 'casp') {
1100 // debug(1,"Speakers:",item_size);
1101 sp -= item_size; // drop down into the array -- don't skip over it
1102 le -= 8;
1103 while (le >= 8) {
1104 uint32_t type = dacp_tlv_crawl(&sp, &item_size);
1105 if (type == 'mdcl') { // drop down into the dictionary -- don't skip over it
1106 // debug(1,">>>> Dictionary:");
1107 sp -= item_size;
1108 le -= 8;
1109 speaker_index++;
1110 if (speaker_index == max_size_of_array) {
1111 return 413; // Payload Too Large -- too many speakers
1112 }
1113 speaker_info[speaker_index].active = 0;
1114 speaker_info[speaker_index].speaker_number = 0;
1115 speaker_info[speaker_index].volume = 0;
1116 speaker_info[speaker_index].name[0] = '\0';
1117 } else {
1118 le -= item_size + 8;
1119 char *t;
1120 // char u;
1121 int32_t r;
1122 int64_t s, v;
1123 switch (type) {
1124 case 'minm':
1125 t = sp - item_size;
1126 strncpy((char *)&speaker_info[speaker_index].name, t,
1127 sizeof(speaker_info[speaker_index].name));
1128 speaker_info[speaker_index].name[sizeof(speaker_info[speaker_index].name) - 1] =
1129 '\0'; // just in case
1130 break;
1131 case 'cmvo':
1132 t = sp - item_size;
1133 r = ntohl(*(uint32_t *)(t));
1134 speaker_info[speaker_index].volume = r;
1135 // debug(1,"The individual volume of speaker \"%s\" is
1136 // \"%d\".",speaker_info[speaker_index].name,r);
1137 break;
1138 case 'msma':
1139 t = sp - item_size;
1140 s = ntohl(*(uint32_t *)(t));
1141 s = s << 32;
1142 t += 4;
1143 v = (ntohl(*(uint32_t *)(t))) & 0xffffffff;
1144 s += v;
1145 speaker_info[speaker_index].speaker_number = s;
1146 // debug(1,"Speaker machine number: %ld",s);
1147 break;
1148
1149 case 'caia':
1150 speaker_info[speaker_index].active = 1;
1151 break;
1152 /*
1153 case 'caip':
1154 case 'cavd':
1155 case 'caiv':
1156 case 'cads':
1157
1158 *(uint32_t *)typestring = htonl(type);
1159 typestring[4] = 0;
1160
1161
1162
1163 t = sp-item_size;
1164 u = *t;
1165 debug(1,"Type: '%s' Value: \"%d\".",typestring,u);
1166 break;
1167 */
1168 default:
1169 break;
1170 }
1171 }
1172 }
1173 // debug(1,"Total of %d speakers found. Here are the active ones:",speaker_index+1);
1174 speaker_count = speaker_index + 1; // number of speaker entries in the array
1175 } else {
1176 debug(1, "Speaker array not found.");
1177 }
1178 /*
1179 int i;
1180 for (i=0;i<le;i++) {
1181 if (*sp < ' ')
1182 debug(1,"%d %02x", i, *sp);
1183 else
1184 debug(1,"%d %02x '%c'", i, *sp,*sp);
1185 sp++;
1186 }
1187 */
1188 } else {
1189 debug(1, "Can't find any content in dacp speakers request");
1190 }
1191 free(server_reply);
1192 server_reply = NULL;
1193 } else {
1194 // debug(1, "Unexpected response %d to dacp speakers request", response);
1195 if (server_reply) {
1196 debug(1, "Freeing response memory.");
1197 free(server_reply);
1198 server_reply = NULL;
1199 }
1200 }
1201 if (actual_speaker_count)
1202 *actual_speaker_count = speaker_count;
1203 return response;
1204 }
1205
1206 int dacp_get_volume(int32_t *the_actual_volume) {
1207 // get the speaker volume information from the DACP source and store it in the metadata_hub
1208 // A volume command has been sent from the client
1209 // let's get the master volume from the DACP remote control
1210 struct dacp_speaker_stuff speaker_info[50];
1211 // we need the overall volume and the speakers information to get this device's relative volume to
1212 // calculate the real volume
1213
1214 int32_t overall_volume = 0;
1215 int32_t actual_volume = 0;
1216 int http_response = dacp_get_client_volume(&overall_volume);
1217 if (http_response == 200) {
1218 // debug(1,"Overall volume is: %u.",overall_volume);
1219 int speaker_count = 0;
1220 http_response = dacp_get_speaker_list((dacp_spkr_stuff *)&speaker_info, 50, &speaker_count);
1221 if (http_response == 200) {
1222 // get our machine number
1223 uint16_t *hn = (uint16_t *)config.ap1_prefix;
1224 uint32_t *ln = (uint32_t *)(config.ap1_prefix + 2);
1225 uint64_t t1 = ntohs(*hn);
1226 uint64_t t2 = ntohl(*ln);
1227 int64_t machine_number = (t1 << 32) + t2; // this form is useful
1228
1229 // Let's find our own speaker in the array and pick up its relative volume
1230 int i;
1231 int32_t relative_volume = 0;
1232 for (i = 0; i < speaker_count; i++) {
1233 if (speaker_info[i].speaker_number == machine_number) {
1234 relative_volume = speaker_info[i].volume;
1235 /*
1236 debug(1,"Our speaker was found with a relative volume of: %u.",relative_volume);
1237
1238 if (speaker_info[i].active)
1239 debug(1,"Our speaker is active.");
1240 else
1241 debug(1,"Our speaker is inactive.");
1242 */
1243 }
1244 }
1245 actual_volume = (overall_volume * relative_volume + 50) / 100;
1246 // debug(1,"Overall volume: %d, relative volume: %d%, actual volume:
1247 // %d.",overall_volume,relative_volume,actual_volume);
1248 // debug(1,"Our actual speaker volume is %d.",actual_volume);
1249 // metadata_hub_modify_prolog();
1250 // metadata_store.speaker_volume = actual_volume;
1251 // metadata_hub_modify_epilog(1);
1252 } else {
1253 debug(2, "Unexpected return code %d from dacp_get_speaker_list.", http_response);
1254 }
1255 } else if ((http_response != 400) && (http_response != 490)) {
1256 debug(3, "Unexpected return code %d from dacp_get_client_volume.", http_response);
1257 }
1258 if (the_actual_volume) {
1259 // debug(1,"dacp_get_volume returns %d.",actual_volume);
1260 *the_actual_volume = actual_volume;
1261 }
1262 return http_response;
1263 }
1264
1265 int dacp_set_volume(int32_t vo) {
1266 int http_response = 492; // argument out of range
1267 if ((vo >= 0) && (vo <= 100)) {
1268 // get the information we need -- the absolute volume, the speaker list, our ID
1269 struct dacp_speaker_stuff speaker_info[50];
1270 int32_t overall_volume;
1271 http_response = dacp_get_client_volume(&overall_volume);
1272 if (http_response == 200) {
1273 int speaker_count;
1274 http_response = dacp_get_speaker_list((dacp_spkr_stuff *)&speaker_info, 50, &speaker_count);
1275 if (http_response == 200) {
1276 // get our machine number
1277 uint16_t *hn = (uint16_t *)config.ap1_prefix;
1278 uint32_t *ln = (uint32_t *)(config.ap1_prefix + 2);
1279 uint64_t t1 = ntohs(*hn);
1280 uint64_t t2 = ntohl(*ln);
1281 int64_t machine_number = (t1 << 32) + t2; // this form is useful
1282
1283 // Let's find our own speaker in the array and pick up its relative volume
1284 int i;
1285 int32_t active_speakers = 0;
1286 for (i = 0; i < speaker_count; i++) {
1287 if (speaker_info[i].speaker_number == machine_number) {
1288 debug(2, "Our speaker number found: %ld with relative volume.", machine_number,
1289 speaker_info[i].volume);
1290 }
1291 if (speaker_info[i].active == 1) {
1292 active_speakers++;
1293 }
1294 }
1295
1296 if (active_speakers == 1) {
1297 // must be just this speaker
1298 debug(2, "Remote-setting volume to %d on just one speaker.", vo);
1299 http_response = dacp_set_include_speaker_volume(machine_number, vo);
1300 } else if (active_speakers == 0) {
1301 debug(2, "No speakers!");
1302 } else {
1303 debug(2, "Speakers: %d, active: %d", speaker_count, active_speakers);
1304 if (vo >= overall_volume) {
1305 debug(2, "Multiple speakers active, but desired new volume is highest");
1306 http_response = dacp_set_include_speaker_volume(machine_number, vo);
1307 } else {
1308 // the desired volume is less than the current overall volume and there is more than
1309 // one
1310 // speaker
1311 // we must find out the highest other speaker volume.
1312 // If the desired volume is less than it, we must set the current_overall volume to
1313 // that
1314 // highest volume
1315 // and set our volume relative to it.
1316 // If the desired volume is greater than the highest current volume, then we can just
1317 // go
1318 // ahead
1319 // with dacp_set_include_speaker_volume, setting the new current overall volume to the
1320 // desired new level
1321 // with the speaker at 100%
1322
1323 int32_t highest_other_volume = 0;
1324 for (i = 0; i < speaker_count; i++) {
1325 if ((speaker_info[i].speaker_number != machine_number) &&
1326 (speaker_info[i].active == 1) &&
1327 (speaker_info[i].volume > highest_other_volume)) {
1328 highest_other_volume = speaker_info[i].volume;
1329 }
1330 }
1331 highest_other_volume = (highest_other_volume * overall_volume + 50) / 100;
1332 if (highest_other_volume <= vo) {
1333 debug(2,
1334 "Highest other volume %d is less than or equal to the desired new volume %d.",
1335 highest_other_volume, vo);
1336 http_response = dacp_set_include_speaker_volume(machine_number, vo);
1337 } else {
1338 debug(2, "Highest other volume %d is greater than the desired new volume %d.",
1339 highest_other_volume, vo);
1340 // if the present overall volume is higher than the highest other volume at present,
1341 // then bring it down to it.
1342 if (overall_volume > highest_other_volume) {
1343 debug(2, "Lower overall volume to new highest volume.");
1344 http_response = dacp_set_include_speaker_volume(
1345 machine_number,
1346 highest_other_volume); // set the overall volume to the highest one
1347 }
1348 if (highest_other_volume != 0) {
1349 int32_t desired_relative_volume =
1350 (vo * 100 + (highest_other_volume / 2)) / highest_other_volume;
1351 debug(2, "Set our speaker volume relative to the highest volume.");
1352 http_response = dacp_set_speaker_volume(
1353 machine_number,
1354 desired_relative_volume); // set the overall volume to the highest one
1355 }
1356 }
1357 }
1358 }
1359 } else {
1360 debug(2, "Can't get speakers list");
1361 }
1362 } else {
1363 debug(2, "Can't get client volume");
1364 }
1365
1366 } else {
1367 debug(2, "Invalid volume: %d -- ignored.", vo);
1368 }
1369 return http_response;
1370 }