From 3198ec426555abd86726125729ec46a3bfe547d5 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Sun, 12 Nov 2017 14:26:10 +0100 Subject: [PATCH] Improve dbus and add some dacp services --- Makefile.am | 2 +- common.h | 2 +- configure.ac | 2 +- dacp.c | 310 ++++++++++++++++++++++++++ dacp.h | 24 ++ dbus/src/dbus_service.c | 161 ++++++++++--- dbus/src/shairport-sync-dbus-client.c | 52 +++-- player.c | 37 ++- player.h | 4 + rtp.c | 90 +------- rtp.h | 2 - rtsp.c | 18 +- shairport.c | 2 +- 13 files changed, 557 insertions(+), 149 deletions(-) create mode 100644 dacp.c create mode 100644 dacp.h diff --git a/Makefile.am b/Makefile.am index f4235e6bb..5f20c9d8b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -82,7 +82,7 @@ shairport_sync_SOURCES += mdns_dns_sd.c endif if USE_DBUS -shairport_sync_SOURCES += dbus/src/dbus_service.c dbus/src/shairportsync.c +shairport_sync_SOURCES += dbus/src/dbus_service.c dbus/src/shairportsync.c dacp.c endif install-exec-hook: diff --git a/common.h b/common.h index 20d142d4c..0e02aa882 100644 --- a/common.h +++ b/common.h @@ -2,9 +2,9 @@ #define _COMMON_H #include +#include #include #include -#include #include "audio.h" #include "config.h" diff --git a/configure.ac b/configure.ac index 9da97fccc..6a4d0c687 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.50]) -AC_INIT([shairport-sync], [3.2d10], [mikebrady@eircom.net]) +AC_INIT([shairport-sync], [3.2d11], [mikebrady@eircom.net]) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR([shairport.c]) AC_CONFIG_HEADERS([config.h]) diff --git a/dacp.c b/dacp.c new file mode 100644 index 000000000..70711251a --- /dev/null +++ b/dacp.c @@ -0,0 +1,310 @@ +/* + * DACP protocol handler. This file is part of Shairport Sync. + * Copyright (c) Mike Brady + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "dacp.h" +#include "common.h" +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +uint32_t dacp_tlv_crawl(char **p, int32_t *length) { + char typecode[5]; + memcpy(typecode, *p, 4); + typecode[4] = '\0'; + uint32_t type = ntohl(*(uint32_t *)*p); + *p += 4; + *length = ntohl(*(int32_t *)*p); + *p += 4 + *length; + // debug(1,"Type seen: '%s' of length %d",typecode,*length); + return type; +} + +ssize_t dacp_send_client_command(rtsp_conn_info *conn, const char *command, char *response, + size_t max_response_length) { + ssize_t reply_size = -1; + if (conn->rtp_running) { + if (conn->dacp_port == 0) { + debug(1, "Can't send a remote request: no valid active remote."); + } else { + + struct addrinfo hints, *res; + int sockfd; + + char message[20000], server_reply[2000], portstring[10], server[256]; + memset(&message, 0, sizeof(message)); + if ((response) && (max_response_length)) + memset(response, 0, max_response_length); + else + memset(&server_reply, 0, sizeof(server_reply)); + memset(&portstring, 0, sizeof(portstring)); + + if (conn->connection_ip_family == AF_INET6) { + sprintf(server, "%s%%%u", conn->client_ip_string, conn->self_scope_id); + } else { + strcpy(server, conn->client_ip_string); + } + + sprintf(portstring, "%u", conn->dacp_port); + + // first, load up address structs with getaddrinfo(): + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + getaddrinfo(server, portstring, &hints, &res); + + // make a socket: + + sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + + if (sockfd == -1) { + debug(1, "Could not create socket"); + } else { + + // connect! + + if (connect(sockfd, res->ai_addr, res->ai_addrlen) < 0) { + debug(1, "connect failed. Error"); + } else { + + sprintf(message, + "GET /ctrl-int/1/%s HTTP/1.1\r\nHost: %s:%u\r\nActive-Remote: %u\r\n\r\n", + command, conn->client_ip_string, conn->dacp_port, conn->dacp_active_remote); + + // Send command + + if (send(sockfd, message, strlen(message), 0) < 0) { + debug(1, "Send failed"); + } + + // Receive a reply from the server + if ((response) && (max_response_length)) + reply_size = recv(sockfd, response, max_response_length, 0); + else + reply_size = recv(sockfd, server_reply, sizeof(server_reply), 0); + if (reply_size < 0) { + debug(1, "recv failed"); + } + close(sockfd); + } + } + } + } else { + debug(1, "Request to pause non-existent play stream -- ignored."); + } + return reply_size; +} + +int32_t dacp_get_client_volume(rtsp_conn_info *conn) { + char server_reply[2000]; + int32_t overall_volume = -1; + + ssize_t reply_size = dacp_send_client_command(conn, "getproperty?properties=dmcp.volume", + server_reply, sizeof(server_reply)); + if (reply_size >= 0) { + if (strstr(server_reply, "HTTP/1.1 200") == server_reply) { // if we get an okay + char *sp = strstr(server_reply, "Content-Length: "); + if (sp) { // there is something there + sp += strlen("Content-Length: "); + int le = atoi(sp); + if (le == 32) { + sp = strstr(sp, "\r") + 4 + 28; + uint32_t *np = (uint32_t *)sp; + overall_volume = ntohl(*np); + // debug(1,"Overall Volume is %d.",overall_volume); + } else { + debug(1, "Can't find the volume tag"); + } + } else { + debug(1, "Can't find any content in volume control request"); + } + } else { + debug(1, "Unexpected response to dacp volume control request"); + } + } else { + debug(1, "Error asking for dacp volume."); + } + return overall_volume; +} + +int dacp_set_include_speaker_volume(rtsp_conn_info *conn, int64_t machine_number, int32_t vo) { + char server_reply[2000]; + int reply = -1; // will bve fixed if there is no problem + char message[1000]; + memset(message, 0, sizeof(message)); + sprintf(message, "setproperty?include-speaker-id=%ld&dmcp.volume=%d", machine_number, vo); + // debug(1,"sending \"%s\"",message); + ssize_t reply_size = dacp_send_client_command(conn, message, server_reply, sizeof(server_reply)); + if (reply_size >= 0) { + if (strstr(server_reply, "HTTP/1.1 204") == server_reply) { + // debug(1,"dacp_set_include_speaker_volume successful."); + reply = 0; + } + } else { + debug(1, "dacp_set_include_speaker_volume unsuccessful."); + } + return reply; +} + +int dacp_set_speaker_volume(rtsp_conn_info *conn, int64_t machine_number, int32_t vo) { + char server_reply[2000]; + int reply = -1; // will bve fixed if there is no problem + char message[1000]; + memset(message, 0, sizeof(message)); + sprintf(message, "setproperty?speaker-id=%ld&dmcp.volume=%d", machine_number, vo); + // debug(1,"sending \"%s\"",message); + ssize_t reply_size = dacp_send_client_command(conn, message, server_reply, sizeof(server_reply)); + if (reply_size >= 0) { + if (strstr(server_reply, "HTTP/1.1 204") == server_reply) { + // debug(1,"dacp_set_speaker_volume successful."); + reply = 0; + } + } else { + debug(1, "dacp_set_speaker_volume unsuccessful."); + } + return reply; +} + +int dacp_get_speaker_list(rtsp_conn_info *conn, dacp_spkr_stuff *speaker_info, + int max_size_of_array) { + char server_reply[2000]; + int speaker_index = -1; // will be incremented before use + int reply = -1; // will bve fixed if there is no problem + ssize_t reply_size = + dacp_send_client_command(conn, "getspeakers", server_reply, sizeof(server_reply)); + if (reply_size >= 0) { + if (strstr(server_reply, "HTTP/1.1 200") == server_reply) { // if we get an okay + char *sp = strstr(server_reply, "Content-Length: "); + if (sp) { // there is something there + sp += strlen("Content-Length: "); + int32_t le = atoi(sp); + int32_t item_size = 0; + sp = strstr(sp, "\r") + 4; + if (dacp_tlv_crawl(&sp, &item_size) == 'casp') { + // debug(1,"Speakers:",item_size); + sp -= item_size; // drop down into the array -- don't skip over it + le -= 8; + while (le >= 8) { + uint32_t type = dacp_tlv_crawl(&sp, &item_size); + if (type == 'mdcl') { // drop down into the dictionary -- don't skip over it + // debug(1,">>>> Dictionary:"); + sp -= item_size; + le -= 8; + speaker_index++; + if (speaker_index == max_size_of_array) + return -1; // too many speakers + speaker_info[speaker_index].active = 0; + speaker_info[speaker_index].speaker_number = 0; + speaker_info[speaker_index].volume = 0; + speaker_info[speaker_index].name = NULL; + } else { + le -= item_size + 8; + char *t; + char u; + int32_t r; + int64_t s, v; + switch (type) { + case 'minm': + t = sp - item_size; + speaker_info[speaker_index].name = strndup(t, item_size); + // debug(1," \"%s\"",speaker_info[speaker_index].name); + break; + /* + case 'cads': + t = sp-item_size; + r = ntohl(*(int32_t*)(t)); + //debug(1,"CADS: \"%d\".",r); + break; + */ + case 'cmvo': + t = sp - item_size; + r = ntohl(*(int32_t *)(t)); + speaker_info[speaker_index].volume = r; + // debug(1,"Volume: \"%d\".",r); + break; + case 'msma': + t = sp - item_size; + s = ntohl(*(uint32_t *)(t)); + s = s << 32; + t += 4; + v = (ntohl(*(uint32_t *)(t))) & 0xffffffff; + s += v; + speaker_info[speaker_index].speaker_number = s; + // debug(1,"Speaker machine number: %ld",s); + break; + + case 'caia': + speaker_info[speaker_index].active = 1; + break; + /* + case 'caip': + case 'cavd': + case 'caiv': + t = sp-item_size; + u = *t; + //debug(1,"Value: \"%d\".",u); + break; + */ + default: + break; + } + } + } + // debug(1,"Total of %d speakers found. Here are the active ones:",speaker_index+1); + reply = speaker_index + 1; // number of speaker entries in the array + } else { + debug(1, "Speaker array not found."); + } + /* + int i; + for (i=0;i + +#include "player.h" + +typedef struct dacp_speaker_stuff { + int64_t speaker_number; + int active; + int32_t volume; + char *name; // this is really just for debugging +} dacp_spkr_stuff; + +uint32_t dacp_tlv_crawl( + char **p, + int32_t *length); // return the code of the next TLV entity and advance the pointer beyond it. +ssize_t dacp_send_client_command(rtsp_conn_info *conn, const char *command, char *response, + size_t max_response_length); +int32_t dacp_get_client_volume(rtsp_conn_info *conn); // return the overall volume from the client +int dacp_set_include_speaker_volume(rtsp_conn_info *conn, int64_t machine_number, int32_t vo); +int dacp_set_speaker_volume(rtsp_conn_info *conn, int64_t machine_number, int32_t vo); +int dacp_get_speaker_list(rtsp_conn_info *conn, dacp_spkr_stuff *speaker_array, + int max_size_of_array); diff --git a/dbus/src/dbus_service.c b/dbus/src/dbus_service.c index b6a537633..e985c3fac 100644 --- a/dbus/src/dbus_service.c +++ b/dbus/src/dbus_service.c @@ -9,6 +9,8 @@ #include "../../rtp.h" +#include "../../dacp.h" + #include "dbus_service.h" gboolean notify_loudness_filter_active_callback(ShairportSync *skeleton, gpointer user_data) { @@ -35,50 +37,143 @@ gboolean notify_loudness_threshold_callback(ShairportSync *skeleton, gpointer us } gboolean notify_volume_callback(ShairportSync *skeleton, gpointer user_data) { - gdouble vo = shairport_sync_get_volume(skeleton); - if (((vo <= 0.0) && (vo >= -30.0)) || (vo == -144.0)) { - debug(1, "Setting volume to %f.", vo); - if (playing_conn) - player_volume_without_notification(vo, playing_conn); - else + gint vo = shairport_sync_get_volume(skeleton); + if ((vo >= 0) && (vo <= 100)) { + if (playing_conn) { + if (vo != + playing_conn + ->dacp_volume) { // this is to stop an infinite loop of setting->checking->setting... + // debug(1, "Remote-setting volume to %d.", vo); + // get the information we need -- the absolute volume, the speaker list, our ID + struct dacp_speaker_stuff speaker_info[50]; + int32_t overall_volume = dacp_get_client_volume(playing_conn); + int speaker_count = + dacp_get_speaker_list(playing_conn, (dacp_spkr_stuff *)&speaker_info, 50); + + // get our machine number + uint16_t *hn = (uint16_t *)config.hw_addr; + uint32_t *ln = (uint32_t *)(config.hw_addr + 2); + uint64_t t1 = ntohs(*hn); + uint64_t t2 = ntohl(*ln); + int64_t machine_number = (t1 << 32) + t2; // this form is useful + + // Let's find our own speaker in the array and pick up its relative volume + int i; + int32_t relative_volume = 0; + int32_t active_speakers = 0; + for (i = 0; i < speaker_count; i++) { + if (speaker_info[i].speaker_number == machine_number) { + // debug(1,"Our speaker number found: %ld.",machine_number); + relative_volume = speaker_info[i].volume; + } + if (speaker_info[i].active == 1) { + active_speakers++; + } + } + + if (active_speakers == 1) { + // must be just this speaker + dacp_set_include_speaker_volume(playing_conn, machine_number, vo); + } else if (active_speakers == 0) { + debug(1, "No speakers!"); + } else { + // debug(1, "Speakers: %d, active: %d",speaker_count,active_speakers); + if (vo >= overall_volume) { + // debug(1,"Multiple speakers active, but desired new volume is highest"); + dacp_set_include_speaker_volume(playing_conn, machine_number, vo); + } else { + // the desired volume is less than the current overall volume and there is more than one + // speaker + // we must find out the highest other speaker volume. + // If the desired volume is less than it, we must set the current_overall volume to that + // highest volume + // and set our volume relative to it. + // If the desired volume is greater than the highest current volume, then we can just go + // ahead + // with dacp_set_include_speaker_volume, setting the new current overall volume to the + // desired new level + // with the speaker at 100% + + int32_t highest_other_volume = 0; + for (i = 0; i < speaker_count; i++) { + if ((speaker_info[i].speaker_number != machine_number) && + (speaker_info[i].active == 1) && + (speaker_info[i].volume > highest_other_volume)) { + highest_other_volume = speaker_info[i].volume; + } + } + highest_other_volume = (highest_other_volume * overall_volume + 50) / 100; + if (highest_other_volume <= vo) { + // debug(1,"Highest other volume %d is less than or equal to the desired new volume + // %d.",highest_other_volume,vo); + dacp_set_include_speaker_volume(playing_conn, machine_number, vo); + } else { + // debug(1,"Highest other volume %d is greater than the desired new volume + // %d.",highest_other_volume,vo); + // if the present overall volume is higher than the highest other volume at present, + // then bring it down to it. + if (overall_volume > highest_other_volume) { + // debug(1,"Lower overall volume to new highest volume."); + dacp_set_include_speaker_volume( + playing_conn, machine_number, + highest_other_volume); // set the overall volume to the highest one + } + int32_t desired_relative_volume = + (vo * 100 + (highest_other_volume / 2)) / highest_other_volume; + // debug(1,"Set our speaker volume relative to the highest volume."); + dacp_set_speaker_volume( + playing_conn, machine_number, + desired_relative_volume); // set the overall volume to the highest one + } + } + } + // } else { + // debug(1, "No need to remote-set volume to %d, as it is already set to this + // value.",playing_conn->dacp_volume); + } + } else debug(1, "no thread playing -- ignored."); } else { - debug(1, "Invalid volume: %f -- ignored.", vo); + debug(1, "Invalid volume: %d -- ignored.", vo); } return TRUE; } -static gboolean on_handle_remote_command(ShairportSync *skeleton, GDBusMethodInvocation *invocation, const gchar *command, gpointer user_data) { - debug(1,"RemoteCommand with command \"%s\".",command); - if (playing_conn) { - char server_reply[2000]; - ssize_t reply_size = rtp_send_client_command(playing_conn,command,server_reply,sizeof(server_reply)); - if (reply_size>=0) { - // not interested in the response. - // if (strstr(server_reply, "HTTP/1.1 204") == server_reply) { - // debug(1,"Client response is No Content"); - // } else if (strstr(server_reply, "HTTP/1.1 200 OK") != server_reply) { - // debug("Client response is OK, with content"); - // } else { - - if (strstr(server_reply, "HTTP/1.1 204") != server_reply) { - debug(1, "Client request to server responded with %d characters starting with this response:", strlen(server_reply)); - int i; - for (i=0;i= 0) { + // not interested in the response. + // if (strstr(server_reply, "HTTP/1.1 204") == server_reply) { + // debug(1,"Client response is No Content"); + // } else if (strstr(server_reply, "HTTP/1.1 200 OK") != server_reply) { + // debug("Client response is OK, with content"); + // } else { + + if (strstr(server_reply, "HTTP/1.1 204") != server_reply) { + debug(1, + "Client request to server responded with %d characters starting with this response:", + strlen(server_reply)); + int i; + for (i = 0; i < reply_size; i++) if (server_reply[i] < ' ') - debug(1,"%d %02x", i, server_reply[i]); + debug(1, "%d %02x", i, server_reply[i]); else - debug(1,"%d %02x '%c'", i, server_reply[i],server_reply[i]); - //sprintf((char *)message + 2 * i, "%02x", server_reply[i]); - //debug(1,"Content is \"%s\".",message); - } - } else { - debug(1,"Error at rtp_send_client_command"); + debug(1, "%d %02x '%c'", i, server_reply[i], server_reply[i]); + // sprintf((char *)message + 2 * i, "%02x", server_reply[i]); + // debug(1,"Content is \"%s\".",message); } } else { - debug(1, "no thread playing -- RemoteCommand ignored."); + debug(1, "Error at rtp_send_client_command"); } - shairport_sync_complete_remote_command(skeleton,invocation); + } else { + debug(1, "no thread playing -- RemoteCommand ignored."); + } + shairport_sync_complete_remote_command(skeleton, invocation); return TRUE; } diff --git a/dbus/src/shairport-sync-dbus-client.c b/dbus/src/shairport-sync-dbus-client.c index 0a30adf63..536667e24 100644 --- a/dbus/src/shairport-sync-dbus-client.c +++ b/dbus/src/shairport-sync-dbus-client.c @@ -54,7 +54,7 @@ void notify_loudness_threshold_callback(ShairportSync *proxy, gpointer user_data void notify_volume_callback(ShairportSync *proxy, gpointer user_data) { gdouble th = shairport_sync_get_volume(proxy); - printf("Client reports volume set to %.2f dB.\n", th); + printf("Client reports volume set to %.2f.\n", th); } pthread_t dbus_thread; @@ -88,35 +88,45 @@ int main(void) { g_signal_connect(proxy, "notify::volume", G_CALLBACK(notify_volume_callback), NULL); g_print("Starting test...\n"); - /* - shairport_sync_set_volume(SHAIRPORT_SYNC(proxy), -20.0); - sleep(1); - shairport_sync_set_volume(SHAIRPORT_SYNC(proxy), -10.0); + + shairport_sync_set_volume(SHAIRPORT_SYNC(proxy), 20); sleep(1); - shairport_sync_set_volume(SHAIRPORT_SYNC(proxy), 0.0); + shairport_sync_set_volume(SHAIRPORT_SYNC(proxy), 100); sleep(1); - shairport_sync_set_volume(SHAIRPORT_SYNC(proxy), -25.0); + shairport_sync_set_volume(SHAIRPORT_SYNC(proxy), 40); sleep(1); - shairport_sync_set_loudness_filter_active(SHAIRPORT_SYNC(proxy), TRUE); - sleep(10); - shairport_sync_set_loudness_threshold(SHAIRPORT_SYNC(proxy), -20.0); - sleep(5); - shairport_sync_set_loudness_filter_active(SHAIRPORT_SYNC(proxy), FALSE); - sleep(5); - shairport_sync_set_loudness_filter_active(SHAIRPORT_SYNC(proxy), TRUE); - sleep(5); - shairport_sync_set_loudness_threshold(SHAIRPORT_SYNC(proxy), -10.0); - sleep(10); - shairport_sync_set_loudness_filter_active(SHAIRPORT_SYNC(proxy), FALSE); + shairport_sync_set_volume(SHAIRPORT_SYNC(proxy), 60); sleep(1); - */ - shairport_sync_call_remote_command(SHAIRPORT_SYNC(proxy), "string",NULL,NULL,NULL); + /* + shairport_sync_set_volume(SHAIRPORT_SYNC(proxy), 10); + sleep(1); + shairport_sync_set_volume(SHAIRPORT_SYNC(proxy), 0); + sleep(1); + shairport_sync_set_volume(SHAIRPORT_SYNC(proxy), 25); + sleep(1); + shairport_sync_set_volume(SHAIRPORT_SYNC(proxy), 100); + sleep(1); + shairport_sync_set_loudness_filter_active(SHAIRPORT_SYNC(proxy), TRUE); + sleep(10); + shairport_sync_set_loudness_threshold(SHAIRPORT_SYNC(proxy), -20.0); + sleep(5); + shairport_sync_set_loudness_filter_active(SHAIRPORT_SYNC(proxy), FALSE); + sleep(5); + shairport_sync_set_loudness_filter_active(SHAIRPORT_SYNC(proxy), TRUE); + sleep(5); + shairport_sync_set_loudness_threshold(SHAIRPORT_SYNC(proxy), -10.0); + sleep(10); + shairport_sync_set_loudness_filter_active(SHAIRPORT_SYNC(proxy), FALSE); + sleep(1); + + shairport_sync_call_remote_command(SHAIRPORT_SYNC(proxy), "string",NULL,NULL,NULL); + */ g_print("Finished test...\n"); g_main_loop_quit(loop); pthread_join(dbus_thread, NULL); printf("exiting program.\n"); g_object_unref(proxy); - + return 0; } diff --git a/player.c b/player.c index 0b3200a88..4376d7eb5 100644 --- a/player.c +++ b/player.c @@ -73,6 +73,7 @@ #endif #ifdef HAVE_DBUS +#include "dacp.h" #include "dbus/src/dbus_service.h" #include "dbus/src/shairportsync.h" #endif @@ -2449,8 +2450,42 @@ void player_volume_without_notification(double airplay_volume, rtsp_conn_info *c void player_volume(double airplay_volume, rtsp_conn_info *conn) { command_set_volume(airplay_volume); + #ifdef HAVE_DBUS - shairport_sync_set_volume(SHAIRPORT_SYNC(skeleton), airplay_volume); + // A volume command has been sent from the client + // let's get the master volume from the DACP remote control + + struct dacp_speaker_stuff speaker_info[50]; + // we need the overall volume and the speakers information to get this device's relative volume to + // calculate the real volume + + int32_t overall_volume = dacp_get_client_volume(conn); + // debug(1,"DACP Volume: %d.",overall_volume); + int speaker_count = dacp_get_speaker_list(conn, (dacp_spkr_stuff *)&speaker_info, 50); + // debug(1,"DACP Speaker Count: %d.",speaker_count); + + // get our machine number + uint16_t *hn = (uint16_t *)config.hw_addr; + uint32_t *ln = (uint32_t *)(config.hw_addr + 2); + uint64_t t1 = ntohs(*hn); + uint64_t t2 = ntohl(*ln); + int64_t machine_number = (t1 << 32) + t2; // this form is useful + + // Let's find our own speaker in the array and pick up its relative volume + int i; + int32_t relative_volume = 0; + for (i = 0; i < speaker_count; i++) { + if (speaker_info[i].speaker_number == machine_number) { + // debug(1,"Our speaker number found: %ld.",machine_number); + relative_volume = speaker_info[i].volume; + } + } + int32_t actual_volume = (overall_volume * relative_volume + 50) / 100; + // debug(1,"Overall volume: %d, relative volume: %d%, actual volume: + // %d.",overall_volume,relative_volume,actual_volume); + // debug(1,"Our actual speaker volume is %d.",actual_volume); + conn->dacp_volume = actual_volume; // this is needed to prevent a loop + shairport_sync_set_volume(SHAIRPORT_SYNC(skeleton), actual_volume); #endif player_volume_without_notification(airplay_volume, conn); } diff --git a/player.h b/player.h index 294c776ac..3f930a182 100644 --- a/player.h +++ b/player.h @@ -108,6 +108,10 @@ typedef struct { AES_KEY aes; #endif +#ifdef HAVE_DBUS + int32_t dacp_volume; +#endif + int amountStuffed; int32_t framesProcessedInThisEpoch; diff --git a/rtp.c b/rtp.c index bd7bb8a3a..14f996182 100644 --- a/rtp.c +++ b/rtp.c @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -37,7 +38,6 @@ #include #include #include -#include #include "common.h" #include "player.h" @@ -88,7 +88,8 @@ void *rtp_audio_receiver(void *arg) { FD_SET(conn->audio_socket, &readfds); do { memory_barrier(); - } while (conn->please_stop == 0 && pselect(conn->audio_socket + 1, &readfds, NULL, NULL, NULL, &pselect_sigset) <= 0); + } while (conn->please_stop == 0 && + pselect(conn->audio_socket + 1, &readfds, NULL, NULL, NULL, &pselect_sigset) <= 0); if (conn->please_stop != 0) { break; } @@ -189,7 +190,8 @@ void *rtp_control_receiver(void *arg) { FD_SET(conn->control_socket, &readfds); do { memory_barrier(); - } while (conn->please_stop == 0 && pselect(conn->control_socket + 1, &readfds, NULL, NULL, NULL, &pselect_sigset) <= 0); + } while (conn->please_stop == 0 && + pselect(conn->control_socket + 1, &readfds, NULL, NULL, NULL, &pselect_sigset) <= 0); if (conn->please_stop != 0) { break; } @@ -333,7 +335,8 @@ void *rtp_timing_sender(void *arg) { FD_SET(conn->timing_socket, &writefds); do { memory_barrier(); - } while (conn->timing_sender_stop == 0 && pselect(conn->timing_socket + 1, NULL, &writefds, NULL, NULL, &pselect_sigset) <= 0); + } while (conn->timing_sender_stop == 0 && + pselect(conn->timing_socket + 1, NULL, &writefds, NULL, NULL, &pselect_sigset) <= 0); if (conn->timing_sender_stop != 0) { break; } @@ -378,7 +381,8 @@ void *rtp_timing_receiver(void *arg) { FD_SET(conn->timing_socket, &readfds); do { memory_barrier(); - } while (conn->please_stop == 0 && pselect(conn->timing_socket + 1, &readfds, NULL, NULL, NULL, &pselect_sigset) <= 0); + } while (conn->please_stop == 0 && + pselect(conn->timing_socket + 1, &readfds, NULL, NULL, NULL, &pselect_sigset) <= 0); if (conn->please_stop != 0) { break; } @@ -797,79 +801,3 @@ void rtp_request_resend(seq_t first, uint32_t count, rtsp_conn_info *conn) { //} } } - -ssize_t rtp_send_client_command(rtsp_conn_info *conn, const char *command, char *response, size_t max_response_length) { - ssize_t reply_size = -1; - if (conn->rtp_running) { - if (conn->dacp_port == 0) { - debug(1, "Can't send a remote request: no valid active remote."); - } else { - - struct addrinfo hints, *res; - int sockfd; - - char message[20000], server_reply[2000], portstring[10], server[256]; - memset(&message, 0, sizeof(message)); - if ((response) && (max_response_length)) - memset(response, 0, max_response_length); - else - memset(&server_reply, 0, sizeof(server_reply)); - memset(&portstring, 0, sizeof(portstring)); - - if (conn->connection_ip_family == AF_INET6) { - sprintf(server, "%s%%%u", conn->client_ip_string, conn->self_scope_id); - } else { - strcpy(server, conn->client_ip_string); - } - - sprintf(portstring, "%u", conn->dacp_port); - - // first, load up address structs with getaddrinfo(): - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - - getaddrinfo(server, portstring, &hints, &res); - - // make a socket: - - sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); - - if (sockfd == -1) { - debug(1, "Could not create socket"); - } else { - - // connect! - - if (connect(sockfd, res->ai_addr, res->ai_addrlen) < 0) { - debug(1, "connect failed. Error"); - } else { - - sprintf(message, - "GET /ctrl-int/1/%s HTTP/1.1\r\nHost: %s:%u\r\nActive-Remote: %u\r\n\r\n", - command, conn->client_ip_string, conn->dacp_port, conn->dacp_active_remote); - - // Send command - - if (send(sockfd, message, strlen(message), 0) < 0) { - debug(1, "Send failed"); - } - - // Receive a reply from the server - if ((response) && (max_response_length)) - reply_size = recv(sockfd, response, max_response_length, 0); - else - reply_size = recv(sockfd, server_reply, sizeof(server_reply), 0); - if (reply_size < 0) { - debug(1, "recv failed"); - } - close(sockfd); - } - } - } - } else { - debug(1, "Request to pause non-existent play stream -- ignored."); - } - return reply_size; -} diff --git a/rtp.h b/rtp.h index c013bd050..641d9623f 100644 --- a/rtp.h +++ b/rtp.h @@ -25,6 +25,4 @@ void clear_reference_timestamp(rtsp_conn_info *conn); uint64_t static local_to_remote_time_jitters; uint64_t static local_to_remote_time_jitters_count; -ssize_t rtp_send_client_command(rtsp_conn_info *conn,const char * command, char *response, size_t max_response_length); - #endif // _RTP_H diff --git a/rtsp.c b/rtsp.c index 4e9749e01..d36c5ce09 100644 --- a/rtsp.c +++ b/rtsp.c @@ -470,7 +470,8 @@ static enum rtsp_read_request_response rtsp_read_request(rtsp_conn_info *conn, FD_SET(conn->fd, &readfds); do { memory_barrier(); - } while (conn->stop == 0 && pselect(conn->fd + 1, &readfds, NULL, NULL, NULL, &pselect_sigset) <= 0); + } while (conn->stop == 0 && + pselect(conn->fd + 1, &readfds, NULL, NULL, NULL, &pselect_sigset) <= 0); if (conn->stop != 0) { debug(3, "RTSP conversation thread %d shutdown requested.", conn->connection_number); reply = rtsp_read_request_response_immediate_shutdown_requested; @@ -549,7 +550,8 @@ static enum rtsp_read_request_response rtsp_read_request(rtsp_conn_info *conn, FD_SET(conn->fd, &readfds); do { memory_barrier(); - } while (conn->stop == 0 && pselect(conn->fd + 1, &readfds, NULL, NULL, NULL, &pselect_sigset) <= 0); + } while (conn->stop == 0 && + pselect(conn->fd + 1, &readfds, NULL, NULL, NULL, &pselect_sigset) <= 0); if (conn->stop != 0) { debug(1, "RTSP shutdown requested."); reply = rtsp_read_request_response_immediate_shutdown_requested; @@ -955,7 +957,8 @@ static void handle_set_parameter_parameter(rtsp_conn_info *conn, rtsp_message *r // to send commands to the source's remote control (if it has one). // `clip` -- the payload is the IP number of the client, i.e. the sender of audio. // 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. +// `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: @@ -1415,12 +1418,12 @@ static void handle_announce(rtsp_conn_info *conn, rtsp_message *req, rtsp_messag } else { int should_wait = 0; - if (! playing_conn) + if (!playing_conn) die("Non existent playing_conn with play_lock enabled."); debug(1, "RTSP Conversation thread %d already playing when asked by thread %d.", playing_conn->connection_number, conn->connection_number); if (playing_conn->stop) { - debug(1,"Playing connection is already shutting down; waiting for it..."); + debug(1, "Playing connection is already shutting down; waiting for it..."); should_wait = 1; } else if (config.allow_session_interruption == 1) { // some other thread has the player ... ask it to relinquish the thread @@ -1443,7 +1446,7 @@ static void handle_announce(rtsp_conn_info *conn, rtsp_message *req, rtsp_messag if (pthread_mutex_trylock(&play_lock) == 0) have_the_player = 1; else - debug(1,"ANNOUNCE failed to get the player"); + debug(1, "ANNOUNCE failed to get the player"); } if (have_the_player) { @@ -1836,7 +1839,8 @@ static void *rtsp_conversation_thread_func(void *pconn) { FD_SET(conn->fd, &writefds); do { memory_barrier(); - } while (conn->stop == 0 && pselect(conn->fd + 1, NULL, &writefds, NULL, NULL, &pselect_sigset) <= 0); + } while (conn->stop == 0 && + pselect(conn->fd + 1, NULL, &writefds, NULL, NULL, &pselect_sigset) <= 0); if (conn->stop == 0) { msg_write_response(conn->fd, resp); } diff --git a/shairport.c b/shairport.c index b2f68b4a6..679216c3c 100644 --- a/shairport.c +++ b/shairport.c @@ -1496,7 +1496,7 @@ int main(int argc, char **argv) { metadata_init(); // create the metadata pipe if necessary #endif -#ifdef HAVE_DBUS +#ifdef HAVE_DBUS // Start up DBUS services after initial settings are all made debug(1, "Starting up D-Bus services"); pthread_create(&dbus_thread, NULL, &dbus_thread_func, NULL);