int mqtt_publish_cover;
int mqtt_enable_remote;
#endif
- uint8_t hw_addr[8]; // only needs 6 but 8 is handy when converting this to a number
+ uint8_t hw_addr[8]; // only needs 6 but 8 is handy when converting this to a number
int port;
int udp_port_base;
int udp_port_range;
char *airplay_device_id; // for the Bonjour advertisement and the GETINFO PList
char *airplay_pin; // non-NULL, 4 char PIN, if required for pairing
char *airplay_pi; // UUID in the Bonjour advertisement and the GETINFO Plist
+ char *nqptp_shared_memory_interface_name; // client name for nqptp service
#endif
} shairport_cfg;
case 'svip':
mqtt_publish("server_ip", data, length);
break;
-
}
}
}
#ifndef NQPTP_SHM_STRUCTURES_H
#define NQPTP_SHM_STRUCTURES_H
-#define STORAGE_ID "/nqptp"
-#define MAX_CLOCKS 32
-#define NQPTP_SHM_STRUCTURES_VERSION 6
+#define NQPTP_SHM_STRUCTURES_VERSION 7
#define NQPTP_CONTROL_PORT 9000
-// the control port will accept a UDP packet with the first letter being:
-// "T", followed by a space and then a space-delimited
-// list of ip numbers, either IPv4 or IPv6
+// The control port will accept a UDP packet with the first letter being:
+// "T", followed by the name of the shared memory interface, followed by
+// a space and then a space-delimited list of ip numbers, either IPv4 or IPv6
// the whole not to exceed 4096 characters in total
// The IPs will become the new list of timing peers, replacing any previous
struct shm_structure {
pthread_mutex_t shm_mutex; // for safely accessing the structure
uint16_t version; // check this is equal to NQPTP_SHM_STRUCTURES_VERSION
- uint32_t flags; // unused
uint64_t master_clock_id; // the current master clock
char master_clock_ip[64]; // where it's coming from
uint64_t local_time; // the time when the offset was calculated
int64_t lt = conn->first_packet_time_to_play - local_time_now;
if (lt < 130000000) {
- debug(1, "Connection %d: Short lead time for first frame %" PRId64 ": %f seconds. Flushing 0.5 seconds",
- conn->connection_number, conn->first_packet_timestamp, lt * 0.000000001);
+ debug(1,
+ "Connection %d: Short lead time for first frame %" PRId64
+ ": %f seconds. Flushing 0.5 seconds",
+ conn->connection_number, conn->first_packet_timestamp, lt * 0.000000001);
do_flush(conn->first_packet_timestamp + 5 * 4410, conn);
} else {
debug(2, "Connection %d: Lead time for first frame %" PRId64 ": %f seconds.",
- conn->connection_number, conn->first_packet_timestamp, lt * 0.000000001);
+ conn->connection_number, conn->first_packet_timestamp, lt * 0.000000001);
}
-/*
- int64_t lateness = local_time_now - conn->first_packet_time_to_play;
- if (lateness > 0) {
- debug(1, "First packet is %" PRId64 " nanoseconds late! Flushing 0.5 seconds",
- lateness);
+ /*
+ int64_t lateness = local_time_now - conn->first_packet_time_to_play;
+ if (lateness > 0) {
+ debug(1, "First packet is %" PRId64 " nanoseconds late! Flushing 0.5
+ seconds", lateness);
- }
-*/
+ }
+ */
}
if (conn->first_packet_time_to_play != 0) {
#include <fcntl.h>
#include <pthread.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#ifdef COMPILE_FOR_FREEBSD
}
void ptp_send_control_message_string(const char *msg) {
- debug(2, "Send control message to NQPTP: \"%s\"", msg);
- int s;
- unsigned short port = htons(NQPTP_CONTROL_PORT);
- struct sockaddr_in server;
+ size_t full_message_size =
+ strlen(config.nqptp_shared_memory_interface_name) + strlen(" ") + strlen(msg) + 1;
+ char *full_message = malloc(full_message_size);
+ if (full_message != NULL) {
+ *full_message = '\0';
+ snprintf(full_message, full_message_size, "%s %s", config.nqptp_shared_memory_interface_name,
+ msg);
+ debug(1, "Send control message to NQPTP: \"%s\"", full_message);
+ int s;
+ unsigned short port = htons(NQPTP_CONTROL_PORT);
+ struct sockaddr_in server;
- /* Create a datagram socket in the internet domain and use the
- * default protocol (UDP).
- */
- if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
- die("Can't open a socket to NQPTP");
- }
+ /* Create a datagram socket in the internet domain and use the
+ * default protocol (UDP).
+ */
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ die("Can't open a socket to NQPTP");
+ }
- /* Set up the server name */
- server.sin_family = AF_INET; /* Internet Domain */
- server.sin_port = port; /* Server Port */
- server.sin_addr.s_addr = 0; /* Server's Address */
+ /* Set up the server name */
+ server.sin_family = AF_INET; /* Internet Domain */
+ server.sin_port = port; /* Server Port */
+ server.sin_addr.s_addr = 0; /* Server's Address */
- /* Send the message in buf to the server */
- if (sendto(s, msg, (strlen(msg) + 1), 0, (struct sockaddr *)&server, sizeof(server)) < 0) {
- die("error sending timing_peer_list to NQPTP");
+ /* Send the message in buf to the server */
+ if (sendto(s, full_message, full_message_size, 0, (struct sockaddr *)&server, sizeof(server)) <
+ 0) {
+ die("error sending timing_peer_list to NQPTP");
+ }
+ /* Deallocate the socket */
+ close(s);
+
+ /* deallocate the message string */
+ free(full_message);
+ } else {
+ debug(1, "Couldn't allocate memory to prepare a qualified ptp control message string.");
}
- /* Deallocate the socket */
- close(s);
}
obfp += 2;
};
*obfp = 0;
-
-
+
+
// get raw timestamp information
// I think that a good way to understand these timestamps is that
// (1) the rtlt below is the timestamp of the frame that should be playing at the
// Thus, (3) the latency can be calculated by subtracting the second from the
// first.
// There must be more to it -- there something missing.
-
+
// In addition, it seems that if the value of the short represented by the second
// pair of bytes in the packet is 7
// then an extra time lag is expected to be added, presumably by
// the AirPort Express.
-
+
// Best guess is that this delay is 11,025 frames.
-
+
uint32_t rtlt = nctohl(&packet[4]); // raw timestamp less latency
uint32_t rt = nctohl(&packet[16]); // raw timestamp
-
+
uint32_t fl = nctohs(&packet[2]); //
-
+
debug(1,"Sync Packet of %d bytes received: \"%s\", flags: %d, timestamps %u and %u,
giving a latency of %d frames.",plen,obf,fl,rt,rtlt,rt-rtlt);
//debug(1,"Monotonic timestamps are: %" PRId64 " and %" PRId64 "
}
int get_play_lock(rtsp_conn_info *conn) {
- debug(2, "Connection %d: request play lock.", conn->connection_number);
+ debug(2, "Connection %d: request play lock.", conn->connection_number);
// returns -1 if it failed, 0 if it succeeded and 1 if it succeeded but
// interrupted an existing session
int response = 0;
ssize_t buflen = 4096;
#ifdef CONFIG_METADATA
if ((config.metadata_enabled != 0) && (config.get_coverart != 0))
- buflen = 1024 * 256; // big enough for typical picture data, which will be base64 encoded
+ buflen = 1024 * 256; // big enough for typical picture data, which will be base64 encoded
#endif
- int release_buffer = 0; // on exit, don't deallocate the buffer if everything was okay
+ int release_buffer = 0; // on exit, don't deallocate the buffer if everything was okay
char *buf = malloc(buflen + 1); // add a NUL at the end
if (!buf) {
warn("Connection %d: rtsp_read_request: can't get a buffer.", conn->connection_number);
char *string;
};
- struct response_t responses[] = {
- {200, "OK"}, {400, "Bad Request"}, {403, "Unauthorized"}, {451, "Unavailable"}, {501, "Not Implemented"}};
- // 451 is really "Unavailable For Legal Reasons"!
+ struct response_t responses[] = {{200, "OK"},
+ {400, "Bad Request"},
+ {403, "Unauthorized"},
+ {451, "Unavailable"},
+ {501, "Not Implemented"}};
+ // 451 is really "Unavailable For Legal Reasons"!
int found = 0;
char *respcode_text = "Unauthorized";
for (i = 0; i < sizeof(responses) / sizeof(struct response_t); i++) {
uint64_t length = 0;
plist_get_data_val(the_item, &buff, &length);
// debug(1,"Item %d, length: %" PRId64 " bytes", item_number, length);
- if ((buff != NULL) && (length >= strlen("bplist00")) && (strstr(buff, "bplist00") == buff)) {
+ if ((buff != NULL) && (length >= strlen("bplist00")) &&
+ (strstr(buff, "bplist00") == buff)) {
// debug(1,"Contains a plist.");
plist_t subsidiary_plist = NULL;
plist_from_memory(buff, length, &subsidiary_plist);
memset(buf, 0, sizeof(buf));
inet_ntop(AF_INET6, (void *)&addr6->sin6_addr, buf, sizeof(buf));
// don't insist there are in the same subnet...
- //if (!different) {
- // debug(1, "%s is in the same subnet as %s.", buf, ip_address);
- plist_array_append_item(addresses, plist_new_string(buf));
+ // if (!different) {
+ // debug(1, "%s is in the same subnet as %s.", buf, ip_address);
+ plist_array_append_item(addresses, plist_new_string(buf));
//}
}
}
// struct sockaddr_in *mask = (struct sockaddr_in *)(iap->ifa_netmask);
// if ((addr->sin_addr.s_addr & mask->sin_addr.s_addr) ==
// (pa4->sin_addr.s_addr & mask->sin_addr.s_addr)) {
- char buf[32];
- memset(buf, 0, sizeof(buf));
- inet_ntop(AF_INET, (void *)&addr->sin_addr, buf, sizeof(buf));
- // no longer insisting they are in the same subnet
- // debug(1, "%s is in the same subnet as %s.", buf, ip_address);
- plist_array_append_item(addresses, plist_new_string(buf));
+ char buf[32];
+ memset(buf, 0, sizeof(buf));
+ inet_ntop(AF_INET, (void *)&addr->sin_addr, buf, sizeof(buf));
+ // no longer insisting they are in the same subnet
+ // debug(1, "%s is in the same subnet as %s.", buf, ip_address);
+ plist_array_append_item(addresses, plist_new_string(buf));
// }
}
}
get_category_string(conn->airplay_stream_category));
mdns_update(NULL, secondary_txt_records);
-
-
resp->respcode = 200;
} else {
debug(1, "SETUP on Connection %d: PTP setup -- no timingPeerInfo plist.",
plist_dict_set_item(setupResponsePlist, "eventPort",
plist_new_uint(conn->local_event_port));
- plist_dict_set_item(setupResponsePlist, "timingPort",
- plist_new_uint(0));
+ plist_dict_set_item(setupResponsePlist, "timingPort", plist_new_uint(0));
cancel_all_RTSP_threads(
remote_control_stream,
conn->connection_number); // kill all the other remote control listeners
#ifdef CONFIG_METADATA
printf(" -M, --metadata-enable ask for metadata from the source and process it.\n");
printf(" --metadata-pipename=PIPE send metadata to PIPE, e.g. "
- "--metadata-pipename=/tmp/%s-metadata.\n", config.appName);
+ "--metadata-pipename=/tmp/%s-metadata.\n",
+ config.appName);
printf(" The default is /tmp/%s-metadata.\n", config.appName);
printf(" -g, --get-coverart send cover art through the metadata pipe.\n");
#endif
strcat(temp_metadata_pipe_name, config.appName);
strcat(temp_metadata_pipe_name, "-metadata");
config.metadata_pipename = strdup(temp_metadata_pipe_name);
- debug(1,"default metadata_pipename is \"%s\".", temp_metadata_pipe_name);
+ debug(1, "default metadata_pipename is \"%s\".", temp_metadata_pipe_name);
}
#endif
char temp_pid_dir[4096];
strcpy(temp_pid_dir, "/var/run/");
strcat(temp_pid_dir, config.appName);
- debug(1,"default pid filename is \"%s\".", temp_pid_dir);
+ debug(1, "default pid filename is \"%s\".", temp_pid_dir);
char *use_this_pid_dir = temp_pid_dir;
#endif
// debug(1,"config.piddir \"%s\".",config.piddir);
#ifdef CONFIG_AIRPLAY_2
if (config.regtype2)
free(config.regtype2);
+ if (config.nqptp_shared_memory_interface_name)
+ free(config.nqptp_shared_memory_interface_name);
+ if (config.airplay_device_id)
+ free(config.airplay_device_id);
+ if (config.airplay_pin)
+ free(config.airplay_pin);
+ if (config.airplay_pi)
+ free(config.airplay_pi);
#endif
#ifdef CONFIG_LIBDAEMON
config_destroy(config.cfg);
if (config.appName)
free(config.appName);
+
// probably should be freeing malloc'ed memory here, including strdup-created strings...
#ifdef CONFIG_LIBDAEMON
// use the start of the config.hw_addr and the PID to generate the default airplay_device_id
uint64_t apid = nctoh64(config.hw_addr);
apid = apid >> 16; // we only use the first 6 bytes but have imported 8.
-
+
int64_t aid;
-
+
// add the airplay_device_id_offset if provided
if (config_lookup_int64(config.cfg, "general.airplay_device_id_offset", &aid)) {
apid += aid;
- }
-
+ }
+
// replace the airplay_device_id with this, if provided
if (config_lookup_int64(config.cfg, "general.airplay_device_id", &aid)) {
apid = aid;
}
-
- char apids[6*2+5+1]; // six pairs of digits, 5 colons and a NUL
- apids[6*2+5] = 0; // NUL termination
+
+ char shared_memory_interface_name[256] = "";
+ snprintf(shared_memory_interface_name, sizeof(shared_memory_interface_name), "/%s-%" PRIx64 "",
+ config.appName, apid);
+ debug(1, "smi name: \"%s\"", shared_memory_interface_name);
+
+ config.nqptp_shared_memory_interface_name = strdup(shared_memory_interface_name);
+
+ char apids[6 * 2 + 5 + 1]; // six pairs of digits, 5 colons and a NUL
+ apids[6 * 2 + 5] = 0; // NUL termination
int i;
char hexchar[] = "0123456789abcdef";
for (i = 5; i >= 0; i--) {
- apids[i*3+1] = hexchar[apid & 0xF];
+ apids[i * 3 + 1] = hexchar[apid & 0xF];
apid = apid >> 4;
- apids[i*3] = hexchar[apid & 0xF];
+ apids[i * 3] = hexchar[apid & 0xF];
apid = apid >> 4;
if (i != 0)
- apids[i*3-1] = ':';
+ apids[i * 3 - 1] = ':';
}
-
+
config.airplay_device_id = strdup(apids);
// now generate a UUID
debug(1, "run_this_after_play_ends action is \"%s\".", strnull(config.cmd_stop));
debug(1, "wait-cmd status is %d.", config.cmd_blocking);
debug(1, "run_this_before_play_begins may return output is %d.", config.cmd_start_returns_output);
- debug(1, "run_this_if_an_unfixable_error_is_detected action is \"%s\".", strnull(config.cmd_unfixable));
- debug(1, "run_this_before_entering_active_state action is \"%s\".", strnull(config.cmd_active_start));
- debug(1, "run_this_after_exiting_active_state action is \"%s\".", strnull(config.cmd_active_stop));
+ debug(1, "run_this_if_an_unfixable_error_is_detected action is \"%s\".",
+ strnull(config.cmd_unfixable));
+ debug(1, "run_this_before_entering_active_state action is \"%s\".",
+ strnull(config.cmd_active_start));
+ debug(1, "run_this_after_exiting_active_state action is \"%s\".",
+ strnull(config.cmd_active_stop));
debug(1, "active_state_timeout is %f seconds.", config.active_state_timeout);
debug(1, "mdns backend \"%s\".", strnull(config.mdns_name));
debug(2, "userSuppliedLatency is %d.", config.userSuppliedLatency);