//============================================================================
// Name : UPnPClient7.cpp
// Author : Majik
// Purpose : Play URL stream on specified Renderer
//============================================================================
#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;
char *rendererName;
bool rendererFound = false;
char *uri;
/* This is our 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);
if (!rendererFound) {
g_print("Renderer %s not found\\n", rendererName);
}
return 0;
}
/*
* Utility function to play URL on Media Renderer
*/
static void play_stream(const GUPnPDeviceProxy *renderer, const char *uri) {
GUPnPServiceProxy *av_transport;
char *friendly_name;
GError *error;
error = NULL;
char metadata[1000];
/* Build metadata, this is very hacky but is good to show raw data */
char *metadata1 =
"<DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/""
" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/""
" xmlns:r="urn:schemas-rinconnetworks-com:metadata-1-0/""
" xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/">"
"<item id="-1" parentID="-1" restricted="true">"
"<res protocolInfo="http-get:*:audio/mpeg:*" >";
char *metadata2 = "</res>"
"<dc:title>UPnPClient7 Test</dc:title>"
"<upnp:class>object.item.audioItem.musicTrack</upnp:class>"
"</item>"
"</DIDL-Lite>";
/* metadata is metadata1 + uri + metadata2 */
strcat(metadata, metadata1);
strcat(metadata, uri);
strcat(metadata, metadata2);
g_print("Trying to play stream...\\n");
/* Get AVTransport service for device */
av_transport = GUPNP_SERVICE_PROXY (gupnp_device_info_get_service(GUPNP_DEVICE_INFO(renderer), AV_TRANSPORT));
friendly_name = gupnp_device_info_get_friendly_name(GUPNP_DEVICE_INFO(renderer));
if ( gupnp_service_proxy_send_action (av_transport, "SetAVTransportURI", &error,
"InstanceID", G_TYPE_UINT, 0,
"CurrentURI", G_TYPE_STRING, uri,
"CurrentURIMetaData", G_TYPE_STRING, metadata,
NULL,NULL) )
{
if ( !gupnp_service_proxy_send_action (av_transport, "Play", &error,
"InstanceID", G_TYPE_UINT, 0,
"Speed", G_TYPE_UINT, 1,
NULL,NULL) )
{
g_warning("Could not start Renderer playing due to error: \'%s\'",error->message);
}
} else {
g_warning("Could not set Transport URI due to error: \'%s\'",error->message);
}
}
/* This is our callback method to handle new devices
* which have been discovered.
*/
static void device_proxy_available_cb(GUPnPControlPoint *cp,
GUPnPDeviceProxy *renderer)
{
/* Add device to list of current devices */
g_hash_table_insert(renderers,
const_cast<char*>(gupnp_device_info_get_udn(GUPNP_DEVICE_INFO(renderer))),
renderer);
if ( strncmp( rendererName, gupnp_device_info_get_friendly_name(GUPNP_DEVICE_INFO(renderer)), strlen(rendererName) ) == 0 ) {
rendererFound = true;
// Do something here
g_print("Renderer %s found\\n", rendererName);
play_stream( renderer, uri);
}
}
/* This is our callback method to handle devices
* which are removed from the network
*/
static void device_proxy_unavailable_cb(GUPnPControlPoint *cp,
GUPnPDeviceProxy *renderer)
{
/* Remove device from list of current devices */
g_hash_table_remove(renderers,
const_cast<char*>(gupnp_device_info_get_udn(GUPNP_DEVICE_INFO(renderer))));
}
/*
* This is the main program
*/
int main (int argc, char **argv)
{
int run_time = 2;
GUPnPContext *context;
GUPnPControlPoint *cp;
/* Check parameters - very basic check */
if ( argc != 3 )
{
g_warning("Wrong number of parameters\\nUsage: UPnPClient7 <renderer Name> <uri>");
return 0;
}
rendererName = argv[1];
uri= argv[2];
/* Required initialisation */
g_thread_init (NULL);
g_type_init ();
/* Create a new GHashTable to store the discovered devices in */
renderers = g_hash_table_new(g_str_hash, g_str_equal);
/* Create a new parser to help with decoding the AV XML data */
lc_parser = gupnp_last_change_parser_new();
/* Create a new GUPnP Context. By here we are using the default GLib main
context, and connecting to the current machine\'s default IP on an
automatically generated port. */
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;
}