+Version 3.2d33
+====
+A new metadata token -- 'pffr' The First Frame of a play session has been Received. Not sure we'll keep it...
+
Version 3.2d30
====
**Enhancements**
// debug(1,"Set alsa volume.");
do_snd_mixer_selem_set_playback_dB_all(alsa_mix_elem, vol);
} else {
- debug(1, "Not setting volume because volume-based mute is active");
+ debug(2, "Not setting volume because volume-based mute is active");
}
}
volume_set_request = 0; // any external request that has been made is now satisfied
pthread_mutex_unlock(&barrier_mutex);
}
-int ss_pthread_mutex_timedlock(pthread_mutex_t *mutex, useconds_t dally_time,
- const char *debugmessage, int debuglevel) {
+void sps_nanosleep(const time_t sec, const long nanosec) {
+ struct timespec req, rem;
+ int result;
+ req.tv_sec = sec;
+ req.tv_nsec = nanosec;
+ do {
+ result = nanosleep(&req, &rem);
+ rem = req;
+ } while ((result == -1) && (errno == EINTR));
+ if (result == -1)
+ debug(1, "Error in sps_nanosleep of %d sec and %ld nanoseconds: %d.", sec, nanosec, errno);
+}
+
+int sps_pthread_mutex_timedlock(pthread_mutex_t *mutex, useconds_t dally_time,
+ const char *debugmessage, int debuglevel) {
- int time_to_wait = dally_time;
+ useconds_t time_to_wait = dally_time;
int r = pthread_mutex_trylock(mutex);
while ((r) && (time_to_wait > 0)) {
- int st = time_to_wait;
+ useconds_t st = time_to_wait;
if (st > 20000)
st = 20000;
- usleep(st);
+ sps_nanosleep(0, st * 1000);
time_to_wait -= st;
r = pthread_mutex_trylock(mutex);
}
extern sigset_t pselect_sigset;
// wait for the specified time in microseconds -- it checks every 20 milliseconds
-int ss_pthread_mutex_timedlock(pthread_mutex_t *mutex, useconds_t dally_time,
- const char *debugmessage, int debuglevel);
+int sps_pthread_mutex_timedlock(pthread_mutex_t *mutex, useconds_t dally_time,
+ const char *debugmessage, int debuglevel);
char *get_version_string(); // mallocs a string space -- remember to free it afterwards
+void sps_nanosleep(const time_t sec,
+ const long nanosec); // waits for this time, even through interruptions
+
#endif // _COMMON_H
int code;
};
-static void *response_realloc(__attribute__((unused)) void *opaque, void *ptr, int size) {
- return realloc(ptr, size);
+void *response_realloc(__attribute__((unused)) void *opaque, void *ptr, int size) {
+ void *t = realloc(ptr, size);
+ if ((t == NULL) && (size != 0))
+ debug(1, "Response realloc of size %d failed!", size);
+ return t;
}
-static void response_body(void *opaque, const char *data, int size) {
+void response_body(void *opaque, const char *data, int size) {
struct HttpResponse *response = (struct HttpResponse *)opaque;
ssize_t space_available = response->malloced_size - response->size;
// only do this one at a time -- not sure it is necessary, but better safe than sorry
- int mutex_reply = ss_pthread_mutex_timedlock(&dacp_conversation_lock, 1000000, command, 1);
+ int mutex_reply = sps_pthread_mutex_timedlock(&dacp_conversation_lock, 2000000, command, 1);
+ // int mutex_reply = pthread_mutex_lock(&dacp_conversation_lock);
if (mutex_reply == 0) {
// debug(1,"dacp_conversation_lock acquired for command \"%s\".",command);
// debug(1, "DACP socket could not be created -- error %d: \"%s\".",errno,strerror(errno));
response.code = 497; // Can't establish a socket to the DACP server
} else {
+ struct timeval tv;
+ tv.tv_sec = 0;
+ tv.tv_usec = 250000;
+ if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof tv) == -1)
+ debug(1, "Error %d setting receive timeout for DACP service.", errno);
+ if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, (const char *)&tv, sizeof tv) == -1)
+ debug(1, "Error %d setting send timeout for DACP service.", errno);
// connect!
// debug(1, "DACP socket created.");
// Send command
// debug(1,"DACP connect message: \"%s\".",message);
+ if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, (const char *)&tv, sizeof tv) == -1)
+ debug(1, "Error %d setting send timeout for DACP service.", errno);
if (send(sockfd, message, strlen(message), 0) != (ssize_t)strlen(message)) {
// debug(1, "Send failed");
response.code = 493; // Client failed to send a message
} else {
- response.body = malloc(2048); // it can resize this if necessary
- response.malloced_size = 2048;
+ response.body = malloc(32768); // it can resize this if necessary
+ response.malloced_size = 32768;
struct http_roundtripper rt;
http_init(&rt, responseFuncs, &response);
int needmore = 1;
int looperror = 0;
- char buffer[1024];
+ char buffer[8192];
memset(buffer, 0, sizeof(buffer));
while (needmore && !looperror) {
const char *data = buffer;
+ if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof tv) == -1)
+ debug(1, "Error %d setting receive timeout for DACP service.", errno);
int ndata = recv(sockfd, buffer, sizeof(buffer), 0);
// debug(1,"Received %d bytes: \"%s\".",ndata,buffer);
if (ndata <= 0) {
// debug(1,"DACP socket closed.");
}
pthread_mutex_unlock(&dacp_conversation_lock);
+ // debug(1,"Sent command\"%s\" with a response body of size %d.",command,response.size);
// debug(1,"dacp_conversation_lock released.");
} else {
- // debug(1, "Could not acquire a lock on the dacp transmit/receive section. Possible
- // timeout?");
+ debug(1, "Could not acquire a lock on the dacp transmit/receive section when attempting to "
+ "send the command \"%s\". Possible timeout?",
+ command);
response.code = 494; // This client is already busy
}
}
// as the conn's connection number
// this is to signify that the player has stopped, but only if another thread (with a different
// index) hasn't already taken over the dacp service
- ss_pthread_mutex_timedlock(
+ sps_pthread_mutex_timedlock(
&dacp_server_information_lock, 500000,
"set_dacp_server_information couldn't get DACP server information lock in 0.5 second!.", 1);
if (dacp_server.players_connection_thread_index == conn->connection_number)
// this will be running on the thread of its caller, not of the conversation thread...
void set_dacp_server_information(rtsp_conn_info *conn) { // tell the DACP conversation thread that
// the port has been set or changed
- ss_pthread_mutex_timedlock(
+ sps_pthread_mutex_timedlock(
&dacp_server_information_lock, 500000,
"set_dacp_server_information couldn't get DACP server information lock in 0.5 second!.", 1);
dacp_server.players_connection_thread_index = conn->connection_number;
dacp_server.active_remote_id = conn->dacp_active_remote;
if (dacp_server.port)
dacp_server.scan_enable = 1;
- else
+ else {
+ debug(1, "DACP server port has been set to zero.");
dacp_server.scan_enable = 0;
+ }
metadata_hub_modify_prolog();
int ch = metadata_store.dacp_server_active != dacp_server.scan_enable;
metadata_store.dacp_server_active = dacp_server.scan_enable;
int32_t revision_number = 1;
while (1) {
int result;
- ss_pthread_mutex_timedlock(
+ sps_pthread_mutex_timedlock(
&dacp_server_information_lock, 500000,
"dacp_monitor_thread_code couldn't get DACP server information lock in 0.5 second!.", 1);
while (dacp_server.scan_enable == 0) {
int32_t the_volume;
result = dacp_get_volume(&the_volume); // just want the http code
if ((result == 496) || (result == 403) || (result == 501)) {
- // debug(1,"Stopping scan because the response to \"dacp_get_volume(NULL)\" is %d.",result);
+ debug(1, "Stopping scan because the response to \"dacp_get_volume(NULL)\" is %d.", result);
dacp_server.scan_enable = 0;
metadata_hub_modify_prolog();
int ch = metadata_store.dacp_server_active != 0;
// debug(1, "DACP Server ID \"%u\" at \"%s:%u\", scan %d.", dacp_server.active_remote_id,
// dacp_server.ip_string, dacp_server.port, scan_index);
- int adv = (result == 200);
- // a result of 200 means the advanced features of, e.g., iTunes, are available
- // so, turn the advanced_dacp_server flag on or off and flag if it's changed.
- metadata_hub_modify_prolog();
- int diff = metadata_store.advanced_dacp_server_active != adv;
- if (diff)
- metadata_store.advanced_dacp_server_active = adv;
- metadata_hub_modify_epilog(diff);
+ if (result != 494) { // this just means that it couldn't send the query because something else
+ // was sending a command
+ int adv = (result == 200);
+ // a result of 200 means the advanced features of, e.g., iTunes, are available
+ // so, turn the advanced_dacp_server flag on or off and flag if it's changed.
+ metadata_hub_modify_prolog();
+ int diff = metadata_store.advanced_dacp_server_active != adv;
+ if (diff) {
+ metadata_store.advanced_dacp_server_active = adv;
+ debug(1, "Setting dacp_server_active to %d because of a response of %d.", adv, result);
+ }
+ metadata_hub_modify_epilog(diff);
+ }
if (result == 200) {
-
metadata_hub_modify_prolog();
int diff = metadata_store.speaker_volume != the_volume;
if (diff)
ShairportSyncAdvancedRemoteControl *shairportSyncAdvancedRemoteControlSkeleton = NULL;
void dbus_metadata_watcher(struct metadata_bundle *argc, __attribute__((unused)) void *userdata) {
- char response[100];
-
- shairport_sync_advanced_remote_control_set_volume(shairportSyncAdvancedRemoteControlSkeleton,
- argc->speaker_volume);
-
- shairport_sync_remote_control_set_airplay_volume(shairportSyncRemoteControlSkeleton,
- argc->airplay_volume);
-
- shairport_sync_remote_control_set_server(shairportSyncRemoteControlSkeleton, argc->client_ip);
-
- if (argc->dacp_server_active) {
- shairport_sync_remote_control_set_available(shairportSyncRemoteControlSkeleton, TRUE);
- } else {
- shairport_sync_remote_control_set_available(shairportSyncRemoteControlSkeleton, FALSE);
- }
-
- if (argc->advanced_dacp_server_active) {
- shairport_sync_advanced_remote_control_set_available(shairportSyncAdvancedRemoteControlSkeleton,
- TRUE);
- } else {
- shairport_sync_advanced_remote_control_set_available(shairportSyncAdvancedRemoteControlSkeleton,
- FALSE);
- }
-
- switch (argc->player_state) {
- case PS_NOT_AVAILABLE:
- shairport_sync_remote_control_set_player_state(shairportSyncRemoteControlSkeleton,
- "Not Available");
- case PS_STOPPED:
- shairport_sync_remote_control_set_player_state(shairportSyncRemoteControlSkeleton, "Stopped");
- break;
- case PS_PAUSED:
- shairport_sync_remote_control_set_player_state(shairportSyncRemoteControlSkeleton, "Paused");
- break;
- case PS_PLAYING:
- shairport_sync_remote_control_set_player_state(shairportSyncRemoteControlSkeleton, "Playing");
- break;
- default:
- debug(1, "This should never happen.");
- }
-
- switch (argc->play_status) {
- case PS_NOT_AVAILABLE:
- shairport_sync_advanced_remote_control_set_playback_status(
- shairportSyncAdvancedRemoteControlSkeleton, "Not Available");
- case PS_STOPPED:
- shairport_sync_advanced_remote_control_set_playback_status(
- shairportSyncAdvancedRemoteControlSkeleton, "Stopped");
- break;
- case PS_PAUSED:
- shairport_sync_advanced_remote_control_set_playback_status(
- shairportSyncAdvancedRemoteControlSkeleton, "Paused");
- break;
- case PS_PLAYING:
- shairport_sync_advanced_remote_control_set_playback_status(
- shairportSyncAdvancedRemoteControlSkeleton, "Playing");
- break;
- default:
- debug(1, "This should never happen.");
- }
-
- switch (argc->repeat_status) {
- case RS_NOT_AVAILABLE:
- strcpy(response,"Not Available");
- break;
- case RS_OFF:
- strcpy(response,"Off");
- break;
- case RS_ONE:
- strcpy(response,"One");
- break;
- case RS_ALL:
- strcpy(response,"All");
- break;
- default:
- debug(1, "This should never happen.");
- }
- const char *th = shairport_sync_advanced_remote_control_get_loop_status(shairportSyncAdvancedRemoteControlSkeleton);
-
- // only set this if it's different
- if ((th==NULL) || (strcasecmp(th,response)!=0)) {
- debug(1,"Loop Status should be changed");
- shairport_sync_advanced_remote_control_set_loop_status(shairportSyncAdvancedRemoteControlSkeleton, response);
- }
-
- switch (argc->shuffle_status) {
- case SS_NOT_AVAILABLE:
- shairport_sync_advanced_remote_control_set_shuffle(shairportSyncAdvancedRemoteControlSkeleton,
- FALSE);
- break;
- case SS_OFF:
- shairport_sync_advanced_remote_control_set_shuffle(shairportSyncAdvancedRemoteControlSkeleton,
- FALSE);
- break;
- case SS_ON:
- shairport_sync_advanced_remote_control_set_shuffle(shairportSyncAdvancedRemoteControlSkeleton,
- TRUE);
- break;
- default:
- debug(1, "This should never happen.");
- }
-
- GVariantBuilder *dict_builder, *aa;
-
- /* Build the metadata array */
- // debug(1,"Build metadata");
- dict_builder = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
-
- // Make up the artwork URI if we have one
- if (argc->cover_art_pathname) {
- char artURIstring[1024];
- sprintf(artURIstring, "file://%s", argc->cover_art_pathname);
- // sprintf(artURIstring,"");
- // debug(1,"artURI String: \"%s\".",artURIstring);
- GVariant *artUrl = g_variant_new("s", artURIstring);
- g_variant_builder_add(dict_builder, "{sv}", "mpris:artUrl", artUrl);
- }
-
- // Add the TrackID if we have one
- if (argc->item_id) {
- char trackidstring[128];
- // debug(1, "Set ID using mper ID: \"%u\".",argc->item_id);
- sprintf(trackidstring, "/org/gnome/ShairportSync/mper_%u", argc->item_id);
- GVariant *trackid = g_variant_new("o", trackidstring);
- g_variant_builder_add(dict_builder, "{sv}", "mpris:trackid", trackid);
- }
-
- // Add the track name if there is one
- if (argc->track_name) {
- // debug(1, "Track name set to \"%s\".", argc->track_name);
- GVariant *trackname = g_variant_new("s", argc->track_name);
- g_variant_builder_add(dict_builder, "{sv}", "xesam:title", trackname);
- }
-
- // Add the album name if there is one
- if (argc->album_name) {
- // debug(1, "Album name set to \"%s\".", argc->album_name);
- GVariant *albumname = g_variant_new("s", argc->album_name);
- g_variant_builder_add(dict_builder, "{sv}", "xesam:album", albumname);
- }
-
- // Add the artists if there are any (actually there will be at most one, but put it in an array)
- if (argc->artist_name) {
- /* Build the artists array */
- // debug(1,"Build artist array");
- aa = g_variant_builder_new(G_VARIANT_TYPE("as"));
- g_variant_builder_add(aa, "s", argc->artist_name);
- GVariant *artists = g_variant_builder_end(aa);
- g_variant_builder_unref(aa);
- g_variant_builder_add(dict_builder, "{sv}", "xesam:artist", artists);
- }
-
- // Add the genres if there are any (actually there will be at most one, but put it in an array)
- if (argc->genre) {
- // debug(1,"Build genre");
- aa = g_variant_builder_new(G_VARIANT_TYPE("as"));
- g_variant_builder_add(aa, "s", argc->genre);
- GVariant *genres = g_variant_builder_end(aa);
- g_variant_builder_unref(aa);
- g_variant_builder_add(dict_builder, "{sv}", "xesam:genre", genres);
- }
-
- GVariant *dict = g_variant_builder_end(dict_builder);
- g_variant_builder_unref(dict_builder);
-
- // debug(1,"Set metadata");
- shairport_sync_remote_control_set_metadata(shairportSyncRemoteControlSkeleton, dict);
-
+ char response[100];
+
+ shairport_sync_advanced_remote_control_set_volume(shairportSyncAdvancedRemoteControlSkeleton,
+ argc->speaker_volume);
+
+ shairport_sync_remote_control_set_airplay_volume(shairportSyncRemoteControlSkeleton,
+ argc->airplay_volume);
+
+ shairport_sync_remote_control_set_server(shairportSyncRemoteControlSkeleton, argc->client_ip);
+
+ if (argc->dacp_server_active) {
+ shairport_sync_remote_control_set_available(shairportSyncRemoteControlSkeleton, TRUE);
+ } else {
+ shairport_sync_remote_control_set_available(shairportSyncRemoteControlSkeleton, FALSE);
+ }
+
+ if (argc->advanced_dacp_server_active) {
+ shairport_sync_advanced_remote_control_set_available(shairportSyncAdvancedRemoteControlSkeleton,
+ TRUE);
+ } else {
+ shairport_sync_advanced_remote_control_set_available(shairportSyncAdvancedRemoteControlSkeleton,
+ FALSE);
+ }
+
+ switch (argc->player_state) {
+ case PS_NOT_AVAILABLE:
+ shairport_sync_remote_control_set_player_state(shairportSyncRemoteControlSkeleton,
+ "Not Available");
+ case PS_STOPPED:
+ shairport_sync_remote_control_set_player_state(shairportSyncRemoteControlSkeleton, "Stopped");
+ break;
+ case PS_PAUSED:
+ shairport_sync_remote_control_set_player_state(shairportSyncRemoteControlSkeleton, "Paused");
+ break;
+ case PS_PLAYING:
+ shairport_sync_remote_control_set_player_state(shairportSyncRemoteControlSkeleton, "Playing");
+ break;
+ default:
+ debug(1, "This should never happen.");
+ }
+
+ switch (argc->play_status) {
+ case PS_NOT_AVAILABLE:
+ shairport_sync_advanced_remote_control_set_playback_status(
+ shairportSyncAdvancedRemoteControlSkeleton, "Not Available");
+ case PS_STOPPED:
+ shairport_sync_advanced_remote_control_set_playback_status(
+ shairportSyncAdvancedRemoteControlSkeleton, "Stopped");
+ break;
+ case PS_PAUSED:
+ shairport_sync_advanced_remote_control_set_playback_status(
+ shairportSyncAdvancedRemoteControlSkeleton, "Paused");
+ break;
+ case PS_PLAYING:
+ shairport_sync_advanced_remote_control_set_playback_status(
+ shairportSyncAdvancedRemoteControlSkeleton, "Playing");
+ break;
+ default:
+ debug(1, "This should never happen.");
+ }
+
+ switch (argc->repeat_status) {
+ case RS_NOT_AVAILABLE:
+ strcpy(response, "Not Available");
+ break;
+ case RS_OFF:
+ strcpy(response, "Off");
+ break;
+ case RS_ONE:
+ strcpy(response, "One");
+ break;
+ case RS_ALL:
+ strcpy(response, "All");
+ break;
+ default:
+ debug(1, "This should never happen.");
+ }
+ const char *th = shairport_sync_advanced_remote_control_get_loop_status(
+ shairportSyncAdvancedRemoteControlSkeleton);
+
+ // only set this if it's different
+ if ((th == NULL) || (strcasecmp(th, response) != 0)) {
+ debug(1, "Loop Status should be changed");
+ shairport_sync_advanced_remote_control_set_loop_status(
+ shairportSyncAdvancedRemoteControlSkeleton, response);
+ }
+
+ switch (argc->shuffle_status) {
+ case SS_NOT_AVAILABLE:
+ shairport_sync_advanced_remote_control_set_shuffle(shairportSyncAdvancedRemoteControlSkeleton,
+ FALSE);
+ break;
+ case SS_OFF:
+ shairport_sync_advanced_remote_control_set_shuffle(shairportSyncAdvancedRemoteControlSkeleton,
+ FALSE);
+ break;
+ case SS_ON:
+ shairport_sync_advanced_remote_control_set_shuffle(shairportSyncAdvancedRemoteControlSkeleton,
+ TRUE);
+ break;
+ default:
+ debug(1, "This should never happen.");
+ }
+
+ GVariantBuilder *dict_builder, *aa;
+
+ /* Build the metadata array */
+ // debug(1,"Build metadata");
+ dict_builder = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
+
+ // Make up the artwork URI if we have one
+ if (argc->cover_art_pathname) {
+ char artURIstring[1024];
+ sprintf(artURIstring, "file://%s", argc->cover_art_pathname);
+ // sprintf(artURIstring,"");
+ // debug(1,"artURI String: \"%s\".",artURIstring);
+ GVariant *artUrl = g_variant_new("s", artURIstring);
+ g_variant_builder_add(dict_builder, "{sv}", "mpris:artUrl", artUrl);
+ }
+
+ // Add the TrackID if we have one
+ if (argc->item_id) {
+ char trackidstring[128];
+ // debug(1, "Set ID using mper ID: \"%u\".",argc->item_id);
+ sprintf(trackidstring, "/org/gnome/ShairportSync/mper_%u", argc->item_id);
+ GVariant *trackid = g_variant_new("o", trackidstring);
+ g_variant_builder_add(dict_builder, "{sv}", "mpris:trackid", trackid);
+ }
+
+ // Add the track name if there is one
+ if (argc->track_name) {
+ // debug(1, "Track name set to \"%s\".", argc->track_name);
+ GVariant *trackname = g_variant_new("s", argc->track_name);
+ g_variant_builder_add(dict_builder, "{sv}", "xesam:title", trackname);
+ }
+
+ // Add the album name if there is one
+ if (argc->album_name) {
+ // debug(1, "Album name set to \"%s\".", argc->album_name);
+ GVariant *albumname = g_variant_new("s", argc->album_name);
+ g_variant_builder_add(dict_builder, "{sv}", "xesam:album", albumname);
+ }
+
+ // Add the artists if there are any (actually there will be at most one, but put it in an array)
+ if (argc->artist_name) {
+ /* Build the artists array */
+ // debug(1,"Build artist array");
+ aa = g_variant_builder_new(G_VARIANT_TYPE("as"));
+ g_variant_builder_add(aa, "s", argc->artist_name);
+ GVariant *artists = g_variant_builder_end(aa);
+ g_variant_builder_unref(aa);
+ g_variant_builder_add(dict_builder, "{sv}", "xesam:artist", artists);
+ }
+
+ // Add the genres if there are any (actually there will be at most one, but put it in an array)
+ if (argc->genre) {
+ // debug(1,"Build genre");
+ aa = g_variant_builder_new(G_VARIANT_TYPE("as"));
+ g_variant_builder_add(aa, "s", argc->genre);
+ GVariant *genres = g_variant_builder_end(aa);
+ g_variant_builder_unref(aa);
+ g_variant_builder_add(dict_builder, "{sv}", "xesam:genre", genres);
+ }
+
+ GVariant *dict = g_variant_builder_end(dict_builder);
+ g_variant_builder_unref(dict_builder);
+
+ // debug(1,"Set metadata");
+ shairport_sync_remote_control_set_metadata(shairportSyncRemoteControlSkeleton, dict);
}
static gboolean on_handle_set_volume(ShairportSyncAdvancedRemoteControl *skeleton,
send_simple_dacp_command("setproperty?dacp.shufflestate=1");
else
send_simple_dacp_command("setproperty?dacp.shufflestate=0");
- ;
return TRUE;
}
char *th = (char *)shairport_sync_advanced_remote_control_get_loop_status(skeleton);
// enum volume_control_profile_type previous_volume_control_profile =
// config.volume_control_profile;
- debug(1,"notify_loop_status_callback called with loop status of \"%s\".",th);
+ debug(1, "notify_loop_status_callback called with loop status of \"%s\".", th);
if (strcasecmp(th, "off") == 0)
send_simple_dacp_command("setproperty?dacp.repeatstate=0");
else if (strcasecmp(th, "one") == 0)
shairportSyncAdvancedRemoteControlSkeleton =
shairport_sync_advanced_remote_control_skeleton_new();
-
+
g_dbus_interface_skeleton_export(
G_DBUS_INTERFACE_SKELETON(shairportSyncAdvancedRemoteControlSkeleton), connection,
"/org/gnome/ShairportSync", NULL);
G_CALLBACK(notify_loop_status_callback), NULL);
add_metadata_watcher(dbus_metadata_watcher, NULL);
-
+
shairport_sync_set_loudness_threshold(SHAIRPORT_SYNC(shairportSyncSkeleton),
config.loudness_reference_volume_db);
"Not Available");
shairport_sync_advanced_remote_control_set_playback_status(
shairportSyncAdvancedRemoteControlSkeleton, "Not Available");
-
- shairport_sync_advanced_remote_control_set_loop_status(
- shairportSyncAdvancedRemoteControlSkeleton, "Not Available");
+
+ shairport_sync_advanced_remote_control_set_loop_status(shairportSyncAdvancedRemoteControlSkeleton,
+ "Not Available");
debug(1, "Shairport Sync native D-Bus service started at \"%s\" on the %s bus.", name,
(config.dbus_service_bus_type == DBT_session) ? "session" : "system");
break;
// these could tell us about play / pause etc. but will only occur if metadata is enabled, so
// we'll just ignore them
- case 'pbeg':
- case 'pend':
- case 'pfls':
- case 'prsm':
- break;
+ case 'pbeg': {
+ metadata_hub_modify_prolog();
+ int changed = (metadata_store.player_state != PS_PLAYING);
+ metadata_store.player_state = PS_PLAYING;
+ metadata_hub_modify_epilog(changed);
+ } break;
+ case 'pend': {
+ metadata_hub_modify_prolog();
+ metadata_store.player_state = PS_STOPPED;
+ // debug(1,"player_stop release track metadata and artwork");
+ metadata_hub_reset_track_metadata();
+ metadata_hub_release_track_artwork();
+ metadata_hub_modify_epilog(1);
+ } break;
+ case 'pfls': {
+ metadata_hub_modify_prolog();
+ int changed = (metadata_store.player_state != PS_PAUSED);
+ metadata_store.player_state = PS_PAUSED;
+ metadata_hub_modify_epilog(changed);
+ } break;
+ case 'pffr': // this is sent when the first frame has been received
+ case 'prsm': {
+ metadata_hub_modify_prolog();
+ int changed = (metadata_store.player_state != PS_PLAYING);
+ metadata_store.player_state = PS_PLAYING;
+ metadata_hub_modify_epilog(changed);
+ } break;
+ case 'pvol': {
+ // Note: it's assumed that the config.airplay volume has already been correctly set.
+ int modified = 0;
+ int32_t actual_volume;
+ int gv = dacp_get_volume(&actual_volume);
+ metadata_hub_modify_prolog();
+ if ((gv == 200) && (metadata_store.speaker_volume != actual_volume)) {
+ metadata_store.speaker_volume = actual_volume;
+ modified = 1;
+ }
+ if (metadata_store.airplay_volume != config.airplay_volume) {
+ metadata_store.airplay_volume = config.airplay_volume;
+ modified = 1;
+ }
+ metadata_hub_modify_epilog(modified); // change
+ } break;
default: {
char typestring[5];
// frames...",curframe->timestamp,seq_diff(ab_read, ab_write));
// say we have started playing here
-#ifdef HAVE_METADATA_HUB
- metadata_hub_modify_prolog();
- int changed = (metadata_store.player_state != PS_PLAYING);
- metadata_store.player_state = PS_PLAYING;
- metadata_hub_modify_epilog(changed);
+#ifdef CONFIG_METADATA
+ send_ssnc_metadata('pffr', NULL, 0,
+ 0); // "first frame received", but don't wait if the queue is locked
#endif
+
if (reference_timestamp) { // if we have a reference time
// debug(1,"First frame seen with timestamp...");
conn->first_packet_timestamp =
#ifdef CONFIG_METADATA
send_ssnc_metadata('prsm', NULL, 0,
0); // "resume", but don't wait if the queue is locked
-#endif
-#ifdef HAVE_METADATA_HUB
- metadata_hub_modify_prolog();
- int changed = (metadata_store.player_state != PS_PLAYING);
- metadata_store.player_state = PS_PLAYING;
- metadata_hub_modify_epilog(changed);
#endif
}
}
int sync_error_out_of_bounds =
0; // number of times in a row that there's been a serious sync error
- // stop looking elsewhere for DACP stuff
- conn->dacp_port = 0;
+// stop looking elsewhere for DACP stuff
#ifdef HAVE_DACP_CLIENT
- set_dacp_server_information(conn); // this will stop scanning until a port is registered by the
+ set_dacp_server_information(conn); // this will start scanning when a port is registered by the
// code initiated by the mdns_dacp_monitor
#endif
// start an mdns/zeroconf thread to look for DACP messages containing our DACP_ID and getting the
void player_volume(double airplay_volume, rtsp_conn_info *conn) {
command_set_volume(airplay_volume);
-#ifdef HAVE_METADATA_HUB
- int modified = 0;
- int32_t actual_volume;
- int gv = dacp_get_volume(&actual_volume);
- metadata_hub_modify_prolog();
- if ((gv == 200) && (metadata_store.speaker_volume != actual_volume)) {
- metadata_store.speaker_volume = actual_volume;
- modified = 1;
- }
- if (metadata_store.airplay_volume != airplay_volume) {
- metadata_store.airplay_volume = airplay_volume;
- modified = 1;
- }
- metadata_hub_modify_epilog(modified); // change
-#endif
player_volume_without_notification(airplay_volume, conn);
}
#ifdef CONFIG_METADATA
send_ssnc_metadata('pfls', NULL, 0, 1);
#endif
-
-#ifdef HAVE_METADATA_HUB
- metadata_hub_modify_prolog();
- int changed = (metadata_store.player_state != PS_PAUSED);
- metadata_store.player_state = PS_PAUSED;
- metadata_hub_modify_epilog(changed);
-#endif
}
int player_play(rtsp_conn_info *conn) {
debug(1, "Error setting stack size for player_thread: %s", strerror(errno));
pthread_create(pt, &tattr, player_thread_func, (void *)conn);
pthread_attr_destroy(&tattr);
-#ifdef HAVE_METADATA_HUB
- metadata_hub_modify_prolog();
- int changed = (metadata_store.player_state != PS_PLAYING);
- metadata_store.player_state = PS_PLAYING;
- metadata_hub_modify_epilog(changed);
-#endif
return 0;
}
command_stop();
free(conn->player_thread);
conn->player_thread = NULL;
-#ifdef HAVE_METADATA_HUB
- metadata_hub_modify_prolog();
- metadata_store.player_state = PS_STOPPED;
- // debug(1,"player_stop release track metadata and artwork");
- metadata_hub_reset_track_metadata();
- metadata_hub_release_track_artwork();
- metadata_hub_modify_epilog(1);
-#endif
} else {
debug(3, "player thread of RTSP conversation %d is already deleted.", conn->connection_number);
}
the_queue->eoq = i;
the_queue->count++;
if (the_queue->count == the_queue->capacity)
- debug(1, "pc_queue is full!");
+ debug(1, "pc_queue is full with %d items in it!", the_queue->count);
rc = pthread_cond_signal(&the_queue->pc_queue_item_added_signal);
if (rc)
debug(1, "Error signalling after pc_queue_add_item");
// 'pend' -- play stream end. No arguments
// 'pfls' -- play stream flush. No arguments
// 'prsm' -- play stream resume. No arguments
+// `pffr` -- the first frame of a play session has been received and has been validly
+//timed.
// 'pvol' -- play volume. The volume is sent as a string --
// "airplay_volume,volume,lowest_volume,highest_volume"
// volume, lowest_volume and highest_volume are given in dB.
// Can be an IPv4 or an IPv6 number.
// `dapo` -- the payload is the port number (as text) on the server to which remote
// control commands should be sent. It is 3689 for iTunes but varies for iOS devices.
-
// A special sub-protocol is used for sending large data items over UDP
// If the payload exceeded 4 MB, it is chunked using the following format:
// "ssnc", "chnk", packet_ix, packet_counts, packet_tag, packet_type, chunked_data.