Improve dbus and add some dacp services
This commit is contained in:
+1
-1
@@ -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:
|
||||
|
||||
@@ -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
@@ -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])
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -108,6 +108,10 @@ typedef struct {
|
||||
AES_KEY aes;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DBUS
|
||||
int32_t dacp_volume;
|
||||
#endif
|
||||
|
||||
int amountStuffed;
|
||||
|
||||
int32_t framesProcessedInThisEpoch;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user