//============================================================================
// Name : UPnPClient6.cpp
// Author : Majik
// Purpose : Monitor transport state of zoneplayers
//============================================================================
#include <string.h>
#include <glib-2.0/glib.h>
#include <libgupnp/gupnp-control-point.h>
#include <libgupnp-av/gupnp-av.h>
#define MEDIA_RENDERER "urn:schemas-upnp-org:device:MediaRenderer:1"
#define AV_TRANSPORT "urn:schemas-upnp-org:service:AVTransport"
static GMainLoop *main_loop;
static GHashTable *renderers;
static GUPnPLastChangeParser *lc_parser;
/* Callback method to terminate the main loop
* after the timeout has expired
*/
static gboolean main_loop_timeout(void *data)
{
g_main_loop_quit (main_loop);
return 0;
}
/* Callback method to print out key details about
* the current track. This comes as a "DIDL" object
*/
static void on_didl_object_available (GUPnPDIDLLiteParser *parser,
GUPnPDIDLLiteObject *object,
gpointer renderer)
{
SoupURI *aa_uri;
SoupURI *url_base;
/* Get the URL base of the renderer */
url_base = (SoupURI *) gupnp_device_info_get_url_base(GUPNP_DEVICE_INFO((GUPnPDeviceProxy *)renderer));
aa_uri = soup_uri_new_with_base(url_base, gupnp_didl_lite_object_get_album_art(object));
g_print(" Artist: %s\\n", gupnp_didl_lite_object_get_creator(object));
g_print(" Title: %s\\n", gupnp_didl_lite_object_get_title(object));
g_print(" Album: %s\\n", gupnp_didl_lite_object_get_album(object));
g_print(" Album Art: %s\\n", soup_uri_to_string (aa_uri, FALSE));
}
/* Callback method to process av transport "last change" events */
static void on_last_change (GUPnPServiceProxy *av_transport,
const char *variable_name,
GValue *value,
gpointer user_data)
{
const char *last_change_xml;
char *state_name;
char *metadata;
GError *lc_error;
GError *error;
const char *udn;
SoupURI *url_base;
static GUPnPDeviceProxy* renderer;
GUPnPDIDLLiteParser *parser;
last_change_xml = g_value_get_string (value);
parser = gupnp_didl_lite_parser_new ();
lc_error = NULL;
error = NULL;
state_name = NULL;
metadata = NULL;
if (gupnp_last_change_parser_parse_last_change(lc_parser, 0, last_change_xml, &lc_error,
"TransportState", G_TYPE_STRING, &state_name,
"CurrentTrackMetaData", G_TYPE_STRING, &metadata,
NULL))
{
/* Look up the UDN in our hashtable to get the renderer */
udn = gupnp_service_info_get_udn(GUPNP_SERVICE_INFO (av_transport));
renderer = (GUPnPDeviceProxy*)g_hash_table_lookup(renderers, udn);
/* Now print the state using the Renderer Friendly Name */
if (state_name != NULL) {
g_print("Device %s is now in state %s\\n",
gupnp_device_info_get_friendly_name(GUPNP_DEVICE_INFO(renderer)),
state_name);
g_free (state_name);
}
/* Get metadata */
if (strlen(metadata) > 5) {
//g_print(" Metadata:%s\\n",metadata);
g_signal_connect (parser, "object-available", G_CALLBACK (on_didl_object_available), (gpointer) renderer);
gupnp_didl_lite_parser_parse_didl (parser, metadata, &error);
if (error) {
g_warning ("%s\\n", error->message);
g_error_free (error);
}
g_object_unref (parser);
g_free (metadata);
}
} else if (lc_error) {
g_warning ("%s\\n", lc_error->message);
g_error_free (lc_error);
}
}
/* Callback method to handle newly discovered devices */
static void device_proxy_available_cb(GUPnPControlPoint *cp,
GUPnPDeviceProxy *renderer)
{
GUPnPServiceProxy *av_transport;
/*
* Add to list of current devices
*/
g_hash_table_insert(renderers,
const_cast<char*>(gupnp_device_info_get_udn(GUPNP_DEVICE_INFO(renderer))),
renderer);
/* Get AVTransport service for device */
av_transport = GUPNP_SERVICE_PROXY (gupnp_device_info_get_service(GUPNP_DEVICE_INFO(renderer), AV_TRANSPORT));
/* Add "LastChange" to the list of states we want to be notified about
* and turn on event subscription
*/
gupnp_service_proxy_add_notify (av_transport,
"LastChange",
G_TYPE_STRING,
on_last_change,
NULL);
gupnp_service_proxy_set_subscribed (av_transport, TRUE);
}
/* callback method to handle devices removed from the network */
static void device_proxy_unavailable_cb(GUPnPControlPoint *cp,
GUPnPDeviceProxy *renderer)
{
/*
* Remove from list of current devices
*/
g_hash_table_remove(renderers,
const_cast<char*>(gupnp_device_info_get_udn(GUPNP_DEVICE_INFO(renderer))));
}
/*
* Main program
*/
int main (int argc, char **argv)
{
int run_time = 0;
GUPnPContext *context;
GUPnPControlPoint *cp;
if ( argc == 2 ) /* Argument is run time in seconds */
{
run_time = atoi(argv[1]);
}
/* Required initialisation */
g_thread_init (NULL);
g_type_init ();
/* Create a new GHashTable to store the discovered devices */
renderers = g_hash_table_new(g_str_hash, g_str_equal);
/* Create a new parser to decode the AV XML data */
lc_parser = gupnp_last_change_parser_new();
/* Create a new GUPnP Context. */
context = gupnp_context_new (NULL, NULL, 0, NULL);
/* Create a Control Point targeting UPnP AV MediaRenderer devices */
cp = gupnp_control_point_new(context, MEDIA_RENDERER);
/* The device-proxy-available signal is emitted when any devices which match
our target are found, so connect to it */
g_signal_connect (cp,
"device-proxy-available",
G_CALLBACK (device_proxy_available_cb),
NULL);
/* The device-proxy-unavailable signal is emitted when any devices which match
our target are removed, so connect to it */
g_signal_connect (cp,
"device-proxy-unavailable",
G_CALLBACK (device_proxy_unavailable_cb),
NULL);
/* Tell the Control Point to start searching */
gssdp_resource_browser_set_active (GSSDP_RESOURCE_BROWSER (cp), TRUE);
/* Set a timeout to finish processing */
if (run_time >0) {
g_timeout_add_seconds (run_time,main_loop_timeout, NULL);
}
/* Enter the main loop. This will start the search and result in callbacks to
device_proxy_available_cb. */
main_loop = g_main_loop_new (NULL, FALSE);
g_main_loop_run (main_loop);
/* Clean up */
g_main_loop_unref (main_loop);
g_object_unref (cp);
g_object_unref (context);
return 0;
}