diff --git a/common.c b/common.c index 306b3c2f..7709e36f 100644 --- a/common.c +++ b/common.c @@ -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; diff --git a/dbus-service.c b/dbus-service.c index 20150abd..ebcd3371 100644 --- a/dbus-service.c +++ b/dbus-service.c @@ -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: diff --git a/mdns_avahi.c b/mdns_avahi.c index b1085bb7..0a02a720 100644 --- a/mdns_avahi.c +++ b/mdns_avahi.c @@ -47,6 +47,13 @@ #include #include +#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); diff --git a/metadata_hub.c b/metadata_hub.c index 4f3fc0f8..9d648fcd 100644 --- a/metadata_hub.c +++ b/metadata_hub.c @@ -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)) { diff --git a/metadata_hub.h b/metadata_hub.h index 8d003703..66368f98 100644 --- a/metadata_hub.h +++ b/metadata_hub.h @@ -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 diff --git a/org.gnome.ShairportSync.xml b/org.gnome.ShairportSync.xml index ae404be7..aa693fd5 100644 --- a/org.gnome.ShairportSync.xml +++ b/org.gnome.ShairportSync.xml @@ -33,6 +33,7 @@ +