Begin to add a basic remote control interface corresponding to the set of commands AirPlay devices seem to obey.

This commit is contained in:
Mike Brady
2018-03-17 19:43:10 +00:00
parent ce403af538
commit 720a86630a
8 changed files with 289 additions and 0 deletions
+1
View File
@@ -34,6 +34,7 @@ shairport-sync.core
/mpris-interface.*
/mpris-player-interface.*
/dbus-diagnostics-interface.*
/dbus-basic-remote-control-interface.*
# Some eclipse project files
.cproject
+10
View File
@@ -111,7 +111,17 @@ dbus-diagnostics-interface.h dbus-diagnostics-interface.c: org.gnome.ShairportS
gdbus-codegen --interface-prefix org.gnome --generate-c-code dbus-diagnostics-interface org.gnome.ShairportSync.Diagnostics.xml
endif
if USE_DBUS_BASIC_REMOTE_CONTROL
shairport_sync_SOURCES += dbus-basic-remote-control.c dbus-basic-remote-control-interface.c
BUILT_SOURCES += dbus-basic-remote-control-interface.h dbus-basic-remote-control-interface.c
# We don't want to install this header
noinst_HEADERS += $(BUILT_SOURCES)
# Correctly clean the generated headers, but keep the xml description
CLEANFILES += $(BUILT_SOURCES)
dbus-basic-remote-control-interface.h dbus-basic-remote-control-interface.c: org.gnome.ShairportSync.Basic.Remote.Control.xml
gdbus-codegen --interface-prefix org.gnome --generate-c-code dbus-basic-remote-control-interface org.gnome.ShairportSync.Basic.Remote.Control.xml
endif
if USE_MPRIS
shairport_sync_SOURCES += mpris-service.c mpris-interface.c mpris-player-interface.c
+3
View File
@@ -280,11 +280,14 @@ AC_ARG_WITH(dbus, [ --with-dbus-interface = include support for a native Shairp
HAS_DBUS=1
AC_DEFINE([HAVE_DBUS], 1, [Needed by the compiler.])
AC_DEFINE([HAVE_DBUS_DIAGNOSTICS], 1, [Needed by the compiler.])
AC_DEFINE([HAVE_DBUS_BASIC_REMOTE_CONTROL], 1, [Needed by the compiler.])
PKG_CHECK_MODULES([GIO_UNIX], [gio-unix-2.0 >= 2.30.0],[CFLAGS="${GIO_UNIX_CFLAGS} ${CFLAGS}" LIBS="${GIO_UNIX_LIBS} ${LIBS}"],[AC_MSG_ERROR(dbus messaging support requires the glib 2.0 library -- libglib2.0-dev suggested!)])
], )
AM_CONDITIONAL([USE_DBUS], [test "x$HAS_DBUS" = "x1"])
# Automatically include the diagnostics interface
AM_CONDITIONAL([USE_DBUS_DIAGNOSTICS], [test "x$HAS_DBUS" = "x1"])
# Automatically include the basic remote control interface
AM_CONDITIONAL([USE_DBUS_BASIC_REMOTE_CONTROL], [test "x$HAS_DBUS" = "x1"])
# Look for dbus test client flag
AC_ARG_WITH(dbus-test-client, [ --with-dbus-test-client = compile dbus test client], [
+233
View File
@@ -0,0 +1,233 @@
#include <stdio.h>
#include <string.h>
#include "config.h"
#include "common.h"
#include "player.h"
#include "rtsp.h"
#include "rtp.h"
#include "dacp.h"
#include "metadata_hub.h"
#include "dbus-basic-remote-control.h"
#include "dbus-basic-remote-control-interface.h"
ShairportSyncBasicRemoteControl *shairportSyncBasicRemoteControlSkeleton;
static gboolean on_handle_fast_forward(ShairportSyncBasicRemoteControl *skeleton, GDBusMethodInvocation *invocation,
__attribute__((unused)) gpointer user_data) {
send_simple_dacp_command("beginff");
shairport_sync_basic_remote_control_complete_fast_forward(skeleton, invocation);
return TRUE;
}
static gboolean on_handle_rewind(ShairportSyncBasicRemoteControl *skeleton, GDBusMethodInvocation *invocation,
__attribute__((unused)) gpointer user_data) {
send_simple_dacp_command("beginrew");
shairport_sync_basic_remote_control_complete_rewind(skeleton, invocation);
return TRUE;
}
static gboolean on_handle_toggle_mute(ShairportSyncBasicRemoteControl *skeleton, GDBusMethodInvocation *invocation,
__attribute__((unused)) gpointer user_data) {
send_simple_dacp_command("mutetoggle");
shairport_sync_basic_remote_control_complete_toggle_mute(skeleton, invocation);
return TRUE;
}
static gboolean on_handle_next(ShairportSyncBasicRemoteControl *skeleton, GDBusMethodInvocation *invocation,
__attribute__((unused)) gpointer user_data) {
send_simple_dacp_command("nextitem");
shairport_sync_basic_remote_control_complete_next(skeleton, invocation);
return TRUE;
}
static gboolean on_handle_previous(ShairportSyncBasicRemoteControl *skeleton, GDBusMethodInvocation *invocation,
__attribute__((unused)) gpointer user_data) {
send_simple_dacp_command("previtem");
shairport_sync_basic_remote_control_complete_previous(skeleton, invocation);
return TRUE;
}
static gboolean on_handle_pause(ShairportSyncBasicRemoteControl *skeleton, GDBusMethodInvocation *invocation,
__attribute__((unused)) gpointer user_data) {
send_simple_dacp_command("pause");
shairport_sync_basic_remote_control_complete_pause(skeleton, invocation);
return TRUE;
}
static gboolean on_handle_play_pause(ShairportSyncBasicRemoteControl *skeleton, GDBusMethodInvocation *invocation,
__attribute__((unused)) gpointer user_data) {
send_simple_dacp_command("playpause");
shairport_sync_basic_remote_control_complete_play_pause(skeleton, invocation);
return TRUE;
}
static gboolean on_handle_play(ShairportSyncBasicRemoteControl *skeleton, GDBusMethodInvocation *invocation,
__attribute__((unused)) gpointer user_data) {
send_simple_dacp_command("play");
shairport_sync_basic_remote_control_complete_play(skeleton, invocation);
return TRUE;
}
static gboolean on_handle_stop(ShairportSyncBasicRemoteControl *skeleton, GDBusMethodInvocation *invocation,
__attribute__((unused)) gpointer user_data) {
send_simple_dacp_command("stop");
shairport_sync_basic_remote_control_complete_stop(skeleton, invocation);
return TRUE;
}
static gboolean on_handle_resume(ShairportSyncBasicRemoteControl *skeleton, GDBusMethodInvocation *invocation,
__attribute__((unused)) gpointer user_data) {
send_simple_dacp_command("playresume");
shairport_sync_basic_remote_control_complete_resume(skeleton, invocation);
return TRUE;
}
/*
static gboolean on_handle_shuffle_songs(ShairportSyncBasicRemoteControl *skeleton, GDBusMethodInvocation *invocation,
__attribute__((unused)) gpointer user_data) {
send_simple_dacp_command("shuffle_songs");
shairport_sync_basic_remote_control_complete_shuffle_songs(skeleton, invocation);
return TRUE;
}
*/
static gboolean on_handle_volume_up(ShairportSyncBasicRemoteControl *skeleton, GDBusMethodInvocation *invocation,
__attribute__((unused)) gpointer user_data) {
send_simple_dacp_command("volumeup");
shairport_sync_basic_remote_control_complete_volume_up(skeleton, invocation);
return TRUE;
}
static gboolean on_handle_volume_down(ShairportSyncBasicRemoteControl *skeleton, GDBusMethodInvocation *invocation,
__attribute__((unused)) gpointer user_data) {
send_simple_dacp_command("volumedown");
shairport_sync_basic_remote_control_complete_volume_down(skeleton, invocation);
return TRUE;
}
/*
gboolean notify_elapsed_time_callback(ShairportSyncDiagnostics *skeleton,
__attribute__((unused)) gpointer user_data) {
// debug(1, "\"notify_elapsed_time_callback\" called.");
if (shairport_sync_diagnostics_get_elapsed_time(skeleton)) {
config.debugger_show_elapsed_time = 1;
debug(1, ">> start including elapsed time in logs");
} else {
config.debugger_show_elapsed_time = 0;
debug(1, ">> stop including elapsed time in logs");
}
return TRUE;
}
gboolean notify_delta_time_callback(ShairportSyncDiagnostics *skeleton,
__attribute__((unused)) gpointer user_data) {
// debug(1, "\"notify_delta_time_callback\" called.");
if (shairport_sync_diagnostics_get_delta_time(skeleton)) {
config.debugger_show_relative_time = 1;
debug(1, ">> start including delta time in logs");
} else {
config.debugger_show_relative_time = 0;
debug(1, ">> stop including delta time in logs");
}
return TRUE;
}
gboolean notify_statistics_callback(ShairportSyncDiagnostics *skeleton,
__attribute__((unused)) gpointer user_data) {
// debug(1, "\"notify_statistics_callback\" called.");
if (shairport_sync_diagnostics_get_statistics(skeleton)) {
debug(1, ">> start logging statistics");
config.statistics_requested = 1;
} else {
debug(1, ">> stop logging statistics");
config.statistics_requested = 0;
}
return TRUE;
}
gboolean notify_verbosity_callback(ShairportSyncDiagnostics *skeleton,
__attribute__((unused)) gpointer user_data) {
gint th = shairport_sync_diagnostics_get_verbosity(skeleton);
if ((th >= 0) && (th <= 3)) {
if (th==0)
debug(1, ">> log verbosity set to %d.", th);
debuglev = th;
debug(1, ">> log verbosity set to %d.", th);
} else {
debug(1, ">> invalid log verbosity: %d. Ignored.", th);
}
return TRUE;
}
*/
void dbus_basic_remote_control_on_dbus_name_acquired(GDBusConnection *connection,
__attribute__((unused)) const gchar *name,
__attribute__((unused)) gpointer user_data) {
debug(1,"dbus_basic_remote_control_on_dbus_name_acquired");
shairportSyncBasicRemoteControlSkeleton = shairport_sync_basic_remote_control_skeleton_new();
g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(shairportSyncBasicRemoteControlSkeleton), connection,
"/org/gnome/ShairportSync", NULL);
g_signal_connect(shairportSyncBasicRemoteControlSkeleton, "handle-fast-forward", G_CALLBACK(on_handle_fast_forward), NULL);
g_signal_connect(shairportSyncBasicRemoteControlSkeleton, "handle-rewind", G_CALLBACK(on_handle_rewind), NULL);
g_signal_connect(shairportSyncBasicRemoteControlSkeleton, "handle-toggle-mute", G_CALLBACK(on_handle_toggle_mute), NULL);
g_signal_connect(shairportSyncBasicRemoteControlSkeleton, "handle-next", G_CALLBACK(on_handle_next), NULL);
g_signal_connect(shairportSyncBasicRemoteControlSkeleton, "handle-previous", G_CALLBACK(on_handle_previous), NULL);
g_signal_connect(shairportSyncBasicRemoteControlSkeleton, "handle-pause", G_CALLBACK(on_handle_pause), NULL);
g_signal_connect(shairportSyncBasicRemoteControlSkeleton, "handle-play-pause", G_CALLBACK(on_handle_play_pause), NULL);
g_signal_connect(shairportSyncBasicRemoteControlSkeleton, "handle-play", G_CALLBACK(on_handle_play), NULL);
g_signal_connect(shairportSyncBasicRemoteControlSkeleton, "handle-stop", G_CALLBACK(on_handle_stop), NULL);
g_signal_connect(shairportSyncBasicRemoteControlSkeleton, "handle-resume", G_CALLBACK(on_handle_resume), NULL);
// g_signal_connect(shairportSyncBasicRemoteControlSkeleton, "handle-shuffle-songs", G_CALLBACK(on_handle_shuffle_songs), NULL);
g_signal_connect(shairportSyncBasicRemoteControlSkeleton, "handle-volume-up", G_CALLBACK(on_handle_volume_up), NULL);
g_signal_connect(shairportSyncBasicRemoteControlSkeleton, "handle-volume-down", G_CALLBACK(on_handle_volume_down), NULL);
/*
shairport_sync_diagnostics_set_verbosity(SHAIRPORT_SYNC_DIAGNOSTICS(shairportSyncDiagnosticsSkeleton),
debuglev);
// debug(2,">> log verbosity is %d.",debuglev);
if (config.statistics_requested == 0) {
shairport_sync_diagnostics_set_statistics(SHAIRPORT_SYNC_DIAGNOSTICS(shairportSyncDiagnosticsSkeleton), FALSE);
// debug(1, ">> statistics logging is off");
} else {
shairport_sync_diagnostics_set_statistics(SHAIRPORT_SYNC_DIAGNOSTICS(shairportSyncDiagnosticsSkeleton), TRUE);
// debug(1, ">> statistics logging is on");
}
if (config.debugger_show_elapsed_time == 0) {
shairport_sync_diagnostics_set_elapsed_time(SHAIRPORT_SYNC_DIAGNOSTICS(shairportSyncDiagnosticsSkeleton), FALSE);
// debug(1, ">> elapsed time is included in log entries");
} else {
shairport_sync_diagnostics_set_elapsed_time(SHAIRPORT_SYNC_DIAGNOSTICS(shairportSyncDiagnosticsSkeleton), TRUE);
// debug(1, ">> elapsed time is not included in log entries");
}
if (config.debugger_show_relative_time == 0) {
shairport_sync_diagnostics_set_delta_time(SHAIRPORT_SYNC_DIAGNOSTICS(shairportSyncDiagnosticsSkeleton), FALSE);
// debug(1, ">> delta time is included in log entries");
} else {
shairport_sync_diagnostics_set_delta_time(SHAIRPORT_SYNC_DIAGNOSTICS(shairportSyncDiagnosticsSkeleton), TRUE);
// debug(1, ">> delta time is not included in log entries");
}
g_signal_connect(shairportSyncDiagnosticsSkeleton, "notify::verbosity",
G_CALLBACK(notify_verbosity_callback), NULL);
g_signal_connect(shairportSyncDiagnosticsSkeleton, "notify::statistics",
G_CALLBACK(notify_statistics_callback), NULL);
g_signal_connect(shairportSyncDiagnosticsSkeleton, "notify::elapsed-time",
G_CALLBACK(notify_elapsed_time_callback), NULL);
g_signal_connect(shairportSyncDiagnosticsSkeleton, "notify::delta-time",
G_CALLBACK(notify_delta_time_callback), NULL);
*/
}
+10
View File
@@ -0,0 +1,10 @@
#ifndef DBUS_BASIC_REMOTE_CONTROL_H
#define DBUS_BASIC_REMOTE_CONTROL_H
#include "dbus-basic-remote-control-interface.h"
ShairportSyncBasicRemoteControl *shairportSyncBasicRemoteControlSkeleton;
void dbus_basic_remote_control_on_dbus_name_acquired(GDBusConnection *connection, const gchar *name, gpointer user_data);
#endif /* #ifndef DBUS_BASIC_REMOTE_CONTROL_ */
+7
View File
@@ -18,6 +18,9 @@
#include "dbus-diagnostics.h"
#endif
#ifdef HAVE_DBUS_BASIC_REMOTE_CONTROL
#include "dbus-basic-remote-control.h"
#endif
void dbus_metadata_watcher(struct metadata_bundle *argc, __attribute__((unused)) void *userdata) {
// debug(1, "DBUS metadata watcher called");
@@ -103,6 +106,10 @@ static void on_dbus_name_acquired(GDBusConnection *connection, const gchar *name
dbus_diagnostics_on_dbus_name_acquired(connection,name,user_data);
#endif
#ifdef HAVE_DBUS_BASIC_REMOTE_CONTROL
dbus_basic_remote_control_on_dbus_name_acquired(connection,name,user_data);
#endif
debug(1, "Shairport Sync native D-Bus service started at \"%s\" on the %s bus.", name,
(config.dbus_service_bus_type == DBT_session) ? "session" : "system");
}
+2
View File
@@ -89,6 +89,8 @@ typedef struct metadata_bundle {
// speaker volume control
// int previous_speaker_volume; // this is needed to prevent a loop
int airplay_volume;
metadata_watcher watchers[number_of_watchers]; // functions to call if the metadata is changed.
void *watchers_data[number_of_watchers]; // their individual data
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8" ?>
<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd">
<interface name="org.gnome.ShairportSync.Basic.Remote.Control">
<method name='FastForward'/>
<method name='Rewind'/>
<method name='ToggleMute'/>
<method name='Next'/>
<method name='Previous'/>
<method name='Pause'/>
<method name='PlayPause'/>
<method name='Play'/>
<method name='Stop'/>
<method name='Resume'/>
<method name='ShuffleSongs'/>
<method name='VolumeUp'/>
<method name='VolumeDown'/>
<property name='Status' type='s' access='read'/>
<property name='AirplayVolume' type='d' access='read'/>
<property name='Metadata' type='a{sv}' access='read'>
<annotation name="org.qtproject.QtDBus.QtTypeName" value="QVariantMap"/>
</property>
</interface>
</node>