Improve dbus and add some dacp services

This commit is contained in:
Mike Brady
2017-11-12 14:26:10 +01:00
parent 532c77eb23
commit 3198ec4265
13 changed files with 559 additions and 151 deletions
+1 -1
View File
@@ -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:
+1 -1
View File
@@ -2,9 +2,9 @@
#define _COMMON_H
#include <libconfig.h>
#include <signal.h>
#include <stdint.h>
#include <sys/socket.h>
#include <signal.h>
#include "audio.h"
#include "config.h"
+1 -1
View File
@@ -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])
+310
View File
@@ -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 <arpa/inet.h>
#include <errno.h>
#include <math.h>
#include <memory.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <unistd.h>
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<le;i++) {
if (*sp < ' ')
debug(1,"%d %02x", i, *sp);
else
debug(1,"%d %02x '%c'", i, *sp,*sp);
sp++;
}
*/
} else {
debug(1, "Can't find any content in dacp speakers request");
}
} else {
debug(1, "Unexpected response to dacp speakers request");
}
} else {
debug(1, "Error asking for dacp speakers.");
}
return reply;
}
+24
View File
@@ -0,0 +1,24 @@
#pragma once
#include "common.h"
#include "config.h"
#include <sys/socket.h>
#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);
+127 -32
View File
@@ -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 {
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 =
dacp_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<reply_size;i++)
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;
}
+34 -24
View File
@@ -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);
sleep(1);
shairport_sync_set_volume(SHAIRPORT_SYNC(proxy), 100);
sleep(1);
shairport_sync_set_volume(SHAIRPORT_SYNC(proxy), 40);
sleep(1);
shairport_sync_set_volume(SHAIRPORT_SYNC(proxy), 60);
sleep(1);
/*
shairport_sync_set_volume(SHAIRPORT_SYNC(proxy), -20.0);
sleep(1);
shairport_sync_set_volume(SHAIRPORT_SYNC(proxy), -10.0);
sleep(1);
shairport_sync_set_volume(SHAIRPORT_SYNC(proxy), 0.0);
sleep(1);
shairport_sync_set_volume(SHAIRPORT_SYNC(proxy), -25.0);
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);
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;
}
+36 -1
View File
@@ -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);
}
+4
View File
@@ -108,6 +108,10 @@ typedef struct {
AES_KEY aes;
#endif
#ifdef HAVE_DBUS
int32_t dacp_volume;
#endif
int amountStuffed;
int32_t framesProcessedInThisEpoch;
+9 -81
View File
@@ -27,6 +27,7 @@
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <math.h>
#include <memory.h>
#include <netdb.h>
@@ -37,7 +38,6 @@
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include <fcntl.h>
#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;
}
-2
View File
@@ -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
+11 -7
View File
@@ -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);
}
+1 -1
View File
@@ -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);