Add a ProgressString peoperty to the native D-Bus interface, improve the mutex lock timing checking
This commit is contained in:
@@ -1013,22 +1013,47 @@ void sps_nanosleep(const time_t sec, const long nanosec) {
|
||||
debug(1, "Error in sps_nanosleep of %d sec and %ld nanoseconds: %d.", sec, nanosec, errno);
|
||||
}
|
||||
|
||||
// Mac OS X doesn't have pthread_mutex_timedlock
|
||||
// Also note that timing must be relative to CLOCK_REALTIME
|
||||
|
||||
#ifdef COMPILE_FOR_LINUX_AND_FREEBSD_AND_CYGWIN_AND_OPENBSD
|
||||
int sps_pthread_mutex_timedlock(pthread_mutex_t *mutex, useconds_t dally_time,
|
||||
const char *debugmessage, int debuglevel) {
|
||||
|
||||
useconds_t time_to_wait = dally_time;
|
||||
int r = pthread_mutex_trylock(mutex);
|
||||
while ((r == EBUSY) && (time_to_wait > 0)) {
|
||||
useconds_t st = time_to_wait;
|
||||
if (st > 20000)
|
||||
st = 20000;
|
||||
sps_nanosleep(0, st * 1000);
|
||||
time_to_wait -= st;
|
||||
r = pthread_mutex_trylock(mutex);
|
||||
}
|
||||
struct timespec tn;
|
||||
clock_gettime(CLOCK_REALTIME, &tn);
|
||||
uint64_t tnfpsec = tn.tv_sec;
|
||||
if (tnfpsec > 0x100000000)
|
||||
warn("clock_gettime seconds overflow!");
|
||||
uint64_t tnfpnsec = tn.tv_nsec;
|
||||
if (tnfpnsec > 0x100000000)
|
||||
warn("clock_gettime nanoseconds seconds overflow!");
|
||||
tnfpsec = tnfpsec << 32;
|
||||
tnfpnsec = tnfpnsec << 32;
|
||||
tnfpnsec = tnfpnsec / 1000000000;
|
||||
|
||||
uint64_t time_now_in_fp = tnfpsec + tnfpnsec; // types okay
|
||||
|
||||
uint64_t dally_time_in_fp = dally_time; // microseconds
|
||||
dally_time_in_fp = (dally_time_in_fp << 32) / 1000000; // convert to fp format
|
||||
uint64_t time_then = time_now_in_fp + dally_time_in_fp;
|
||||
|
||||
uint64_t time_then_nsec = time_then & 0xffffffff; // remove integral part
|
||||
time_then_nsec = time_then_nsec * 1000000000; // multiply fractional part to nanoseconds
|
||||
|
||||
struct timespec timeoutTime;
|
||||
|
||||
time_then = time_then >> 32; // get the seconds
|
||||
time_then_nsec = time_then_nsec >> 32; // and the nanoseconds
|
||||
|
||||
timeoutTime.tv_sec = time_then;
|
||||
timeoutTime.tv_nsec = time_then_nsec;
|
||||
|
||||
int r = pthread_mutex_timedlock(mutex, &timeoutTime);
|
||||
|
||||
if ((r != 0) && (debugmessage != NULL)) {
|
||||
char errstr[1000];
|
||||
if (r == EBUSY)
|
||||
if (r == ETIMEDOUT)
|
||||
debug(debuglevel,
|
||||
"waiting for a mutex, maximum expected time of %d microseconds exceeded \"%s\".",
|
||||
dally_time, debugmessage);
|
||||
@@ -1038,6 +1063,36 @@ int sps_pthread_mutex_timedlock(pthread_mutex_t *mutex, useconds_t dally_time,
|
||||
}
|
||||
return r;
|
||||
}
|
||||
#endif
|
||||
#ifdef COMPILE_FOR_OSX
|
||||
int sps_pthread_mutex_timedlock(pthread_mutex_t *mutex, useconds_t dally_time,
|
||||
const char *debugmessage, int debuglevel) {
|
||||
|
||||
useconds_t time_to_wait = dally_time;
|
||||
int r = pthread_mutex_trylock(mutex);
|
||||
while ((r == EBUSY) && (time_to_wait > 0)) {
|
||||
useconds_t st = time_to_wait;
|
||||
if (st > 1000)
|
||||
st = 1000;
|
||||
sps_nanosleep(0, st * 1000);
|
||||
time_to_wait -= st;
|
||||
r = pthread_mutex_trylock(mutex);
|
||||
}
|
||||
if ((r != 0) && (debugmessage != NULL)) {
|
||||
char errstr[1000];
|
||||
if (r == EBUSY) {
|
||||
debug(debuglevel,
|
||||
"waiting for a mutex, maximum expected time of %d microseconds exceeded \"%s\".",
|
||||
dally_time, debugmessage);
|
||||
r = ETIMEDOUT; // for compatibility
|
||||
} else {
|
||||
debug(debuglevel, "error %d: \"%s\" waiting for a mutex: \"%s\".", r,
|
||||
strerror_r(r, errstr, sizeof(errstr)), debugmessage);
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
#endif
|
||||
|
||||
int _debug_mutex_lock(pthread_mutex_t *mutex, useconds_t dally_time, const char *filename,
|
||||
const int line, int debuglevel) {
|
||||
@@ -1047,7 +1102,7 @@ int _debug_mutex_lock(pthread_mutex_t *mutex, useconds_t dally_time, const char
|
||||
snprintf(dstring, sizeof(dstring), "%s:%d", filename, line);
|
||||
debug(3, "debug_mutex_lock at \"%s\".", dstring);
|
||||
int result = sps_pthread_mutex_timedlock(mutex, dally_time, dstring, debuglevel);
|
||||
if (result == EBUSY) {
|
||||
if (result == ETIMEDOUT) {
|
||||
result = pthread_mutex_lock(mutex);
|
||||
uint64_t time_delay = get_absolute_time_in_fp() - time_at_start;
|
||||
uint64_t divisor = (uint64_t)1 << 32;
|
||||
|
||||
@@ -43,6 +43,17 @@ void dbus_metadata_watcher(struct metadata_bundle *argc, __attribute__((unused))
|
||||
shairport_sync_advanced_remote_control_set_available(shairportSyncAdvancedRemoteControlSkeleton,
|
||||
FALSE);
|
||||
}
|
||||
|
||||
if (argc->progress_string) {
|
||||
// debug(1, "Check progress string");
|
||||
th = shairport_sync_remote_control_get_progress_string(
|
||||
shairportSyncRemoteControlSkeleton);
|
||||
if ((th == NULL) || (strcasecmp(th, argc->progress_string) != 0)) {
|
||||
// debug(1, "Progress string should be changed");
|
||||
shairport_sync_remote_control_set_progress_string(
|
||||
shairportSyncRemoteControlSkeleton, argc->progress_string);
|
||||
}
|
||||
}
|
||||
|
||||
switch (argc->player_state) {
|
||||
case PS_NOT_AVAILABLE:
|
||||
|
||||
+31
-9
@@ -47,6 +47,13 @@
|
||||
#include <avahi-client/lookup.h>
|
||||
#include <avahi-common/alternative.h>
|
||||
|
||||
#define check_avahi_response(debugLevelArg, veryUnLikelyArgumentName) \
|
||||
{ \
|
||||
int rc = veryUnLikelyArgumentName; \
|
||||
if (rc) \
|
||||
debug(debugLevelArg, "avahi call response %d at __FILE__, __LINE__)", rc); \
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
AvahiThreadedPoll *service_poll;
|
||||
AvahiClient *service_client;
|
||||
@@ -104,7 +111,7 @@ static void resolve_callback(AvahiServiceResolver *r, AVAHI_GCC_UNUSED AvahiIfIn
|
||||
}
|
||||
}
|
||||
}
|
||||
avahi_service_resolver_free(r);
|
||||
check_avahi_response(1, avahi_service_resolver_free(r));
|
||||
}
|
||||
static void browse_callback(AvahiServiceBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol,
|
||||
AvahiBrowserEvent event, const char *name, const char *type,
|
||||
@@ -166,8 +173,12 @@ static void egroup_callback(AvahiEntryGroup *g, AvahiEntryGroupState state,
|
||||
|
||||
/* A service name collision with a remote service
|
||||
* happened. Let's pick a new name */
|
||||
debug(1, "avahi name collision -- look for another");
|
||||
n = avahi_alternative_service_name(service_name);
|
||||
avahi_free(service_name);
|
||||
if (service_name)
|
||||
avahi_free(service_name);
|
||||
else
|
||||
debug(1, "avahi attempt to free a NULL service name");
|
||||
service_name = n;
|
||||
|
||||
debug(2, "avahi: service name collision, renaming service to '%s'", service_name);
|
||||
@@ -248,7 +259,7 @@ static void client_callback(AvahiClient *c, AvahiClientState state,
|
||||
switch (state) {
|
||||
case AVAHI_CLIENT_S_REGISTERING:
|
||||
if (group)
|
||||
avahi_entry_group_reset(group);
|
||||
check_avahi_response(1, avahi_entry_group_reset(group));
|
||||
break;
|
||||
|
||||
case AVAHI_CLIENT_S_RUNNING:
|
||||
@@ -260,8 +271,12 @@ static void client_callback(AvahiClient *c, AvahiClientState state,
|
||||
debug(1, "avahi: client failure: %s", avahi_strerror(err));
|
||||
|
||||
if (err == AVAHI_ERR_DISCONNECTED) {
|
||||
debug(1, "avahi client -- we have been disconnected, so let's reconnect.");
|
||||
/* We have been disconnected, so lets reconnect */
|
||||
avahi_client_free(c);
|
||||
if (c)
|
||||
avahi_client_free(c);
|
||||
else
|
||||
debug(1, "Attempt to free NULL avahi client");
|
||||
c = NULL;
|
||||
group = NULL;
|
||||
|
||||
@@ -307,8 +322,12 @@ static void service_client_callback(AvahiClient *c, AvahiClientState state, void
|
||||
debug(1, "avahi: service client failure: %s", avahi_strerror(err));
|
||||
|
||||
if (err == AVAHI_ERR_DISCONNECTED) {
|
||||
debug(1, "avahi client has been disconnected, so reconnect");
|
||||
/* We have been disconnected, so lets reconnect */
|
||||
avahi_client_free(c);
|
||||
if (c)
|
||||
avahi_client_free(c);
|
||||
else
|
||||
debug(1, "avahi attempt to free a NULL client");
|
||||
c = NULL;
|
||||
|
||||
if (!(dbs->service_client =
|
||||
@@ -366,18 +385,21 @@ static void avahi_unregister(void) {
|
||||
debug(1, "avahi: avahi_unregister.");
|
||||
if (tpoll) {
|
||||
avahi_threaded_poll_stop(tpoll);
|
||||
|
||||
|
||||
if (client) {
|
||||
avahi_client_free(client);
|
||||
client = NULL;
|
||||
};
|
||||
|
||||
} else {
|
||||
debug(1, "avahi attempting to unregister a NULL client");
|
||||
}
|
||||
avahi_threaded_poll_free(tpoll);
|
||||
tpoll = NULL;
|
||||
}
|
||||
|
||||
if (service_name)
|
||||
free(service_name);
|
||||
else
|
||||
debug(1, "avahi attempt to free NULL service name");
|
||||
service_name = NULL;
|
||||
}
|
||||
|
||||
@@ -418,7 +440,7 @@ void *avahi_dacp_monitor(char *dacp_id) {
|
||||
if (!(dbs->service_browser =
|
||||
avahi_service_browser_new(dbs->service_client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
|
||||
"_dacp._tcp", NULL, 0, browse_callback, (void *)dbs))) {
|
||||
warn("Failed to create service browser: %s\n",
|
||||
warn("failed to create avahi service browser: %s\n",
|
||||
avahi_strerror(avahi_client_errno(dbs->service_client)));
|
||||
avahi_client_free(dbs->service_client);
|
||||
avahi_threaded_poll_free(dbs->service_poll);
|
||||
|
||||
@@ -517,6 +517,16 @@ void metadata_hub_process_metadata(uint32_t type, uint32_t code, char *data, uin
|
||||
}
|
||||
break;
|
||||
*/
|
||||
case 'prgr':
|
||||
if ((metadata_store.progress_string == NULL) ||
|
||||
(strncmp(metadata_store.progress_string, data, length) != 0)) {
|
||||
metadata_hub_modify_prolog();
|
||||
release_char_string(&metadata_store.progress_string);
|
||||
metadata_store.progress_string = strndup(data, length);
|
||||
debug(2, "MH Progress String set to: \"%s\"", metadata_store.progress_string);
|
||||
metadata_hub_modify_epilog(1);
|
||||
}
|
||||
break;
|
||||
case 'svip':
|
||||
if ((metadata_store.server_ip == NULL) ||
|
||||
(strncmp(metadata_store.server_ip, data, length) != 0)) {
|
||||
|
||||
@@ -56,6 +56,7 @@ typedef struct metadata_bundle {
|
||||
char *client_ip; // IP number used by the audio source (i.e. the "client"), which is also the DACP
|
||||
// server
|
||||
char *server_ip; // IP number used by Shairport Sync
|
||||
char *progress_string; // progress string, emitted by the source from time to time
|
||||
int player_thread_active; // true if a play thread is running
|
||||
int dacp_server_active; // true if there's a reachable DACP server (assumed to be the Airplay
|
||||
// client) ; false otherwise
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
<method name='VolumeUp'/>
|
||||
<method name='VolumeDown'/>
|
||||
<property name='PlayerState' type='s' access='read'/>
|
||||
<property name='ProgressString' type='s' access='read'/>
|
||||
<property name='Server' type='s' access='read'/>
|
||||
<property name='AirplayVolume' type='d' access='read'/>
|
||||
<property name='Metadata' type='a{sv}' access='read'>
|
||||
|
||||
Reference in New Issue
Block a user