Commit 0e45933d authored by Mirsal Ennaime's avatar Mirsal Ennaime

UPnP services discovery: Merge the SoC 2008 fixes, remove the Cookie class and...

UPnP services discovery: Merge the SoC 2008 fixes, remove the Cookie class and use the ordinary mechanisms instead
parent 120c15e5
......@@ -6,6 +6,7 @@
*
* Authors: Rémi Denis-Courmont <rem # videolan.org> (original plugin)
* Christian Henz <henz # c-lab.de>
* Mirsal Ennaime <mirsal dot ennaime at gmail dot com>
*
* UPnP Plugin using the Intel SDK (libupnp) instead of CyberLink
*
......@@ -46,26 +47,6 @@
#include <vlc_plugin.h>
#include <vlc_playlist.h>
/*****************************************************************************
* Module descriptor
*****************************************************************************/
static int Open( vlc_object_t* );
static void Close( vlc_object_t* );
vlc_module_begin ()
set_shortname( "UPnP" )
set_description( N_( "Universal Plug'n'Play discovery ( Intel SDK )" ) )
set_category( CAT_PLAYLIST )
set_subcategory( SUBCAT_PLAYLIST_SD )
set_capability( "services_discovery", 0 )
set_callbacks( Open, Close )
vlc_module_end ()
/*****************************************************************************
* Local prototypes
*****************************************************************************/
// Constants
const char* MEDIA_SERVER_DEVICE_TYPE = "urn:schemas-upnp-org:device:MediaServer:1";
......@@ -78,24 +59,21 @@ class MediaServer;
class MediaServerList;
class Item;
class Container;
class Lockable;
// Cookie that is passed to the callback
// VLC handle
typedef struct
struct services_discovery_sys_t
{
services_discovery_t* serviceDiscovery;
UpnpClient_Handle clientHandle;
MediaServerList* serverList;
Lockable* lock;
} Cookie;
};
// Class definitions...
class Lockable
{
public:
Lockable()
{
vlc_mutex_init( &_mutex );
......@@ -110,6 +88,7 @@ public:
void unlock() { vlc_mutex_unlock( &_mutex ); }
private:
vlc_mutex_t _mutex;
};
......@@ -119,13 +98,13 @@ class Locker
public:
Locker( Lockable* l )
{
_lockable = l;
_lockable->lock();
_lockable = l;
_lockable->lock();
}
~Locker()
{
_lockable->unlock();
_lockable->unlock();
}
private:
......@@ -137,9 +116,14 @@ class MediaServer
{
public:
static void parseDeviceDescription( IXML_Document* doc, const char* location, Cookie* cookie );
static void parseDeviceDescription( IXML_Document* doc,
const char* location,
services_discovery_t* p_sd );
MediaServer( const char* UDN, const char* friendlyName, Cookie* cookie );
MediaServer( const char* UDN,
const char* friendlyName,
services_discovery_t* p_sd );
~MediaServer();
const char* getUDN() const;
......@@ -162,9 +146,11 @@ private:
bool _fetchContents( Container* parent );
void _buildPlaylist( Container* container );
IXML_Document* _browseAction( const char*, const char*, const char*, const char*, const char*, const char* );
IXML_Document* _browseAction( const char*, const char*,
const char*, const char*, const char*, const char* );
Cookie* _cookie;
services_discovery_t* _p_sd;
Container* _contents;
playlist_item_t* _playlistNode;
......@@ -184,7 +170,7 @@ class MediaServerList
{
public:
MediaServerList( Cookie* cookie );
MediaServerList( services_discovery_t* p_sd );
~MediaServerList();
bool addServer( MediaServer* s );
......@@ -195,7 +181,7 @@ public:
private:
Cookie* _cookie;
services_discovery_t* _p_sd;
std::vector<MediaServer*> _list;
};
......@@ -205,7 +191,10 @@ class Item
{
public:
Item( Container* parent, const char* objectID, const char* title, const char* resource );
Item( Container* parent,
const char* objectID,
const char* title,
const char* resource );
const char* getObjectID() const;
const char* getTitle() const;
......@@ -260,112 +249,109 @@ private:
};
// VLC handle
struct services_discovery_sys_t
{
playlist_t *p_playlist;
playlist_item_t *p_node_cat;
playlist_item_t *p_node_one;
Cookie cookie;
};
// VLC callback prototypes
static playlist_t *pl_Get( services_discovery_t *p_sd )
{
return p_sd->p_sys->p_playlist;
}
static int Open( vlc_object_t* );
static void Close( vlc_object_t* );
static void Run( services_discovery_t *p_sd );
// Module descriptor
vlc_module_begin();
set_shortname( "UPnP" );
set_description( N_( "Universal Plug'n'Play discovery ( Intel SDK )" ) );
set_category( CAT_PLAYLIST );
set_subcategory( SUBCAT_PLAYLIST_SD );
set_capability( "services_discovery", 0 );
set_callbacks( Open, Close );
vlc_module_end();
// More prototypes...
static int Callback( Upnp_EventType eventType, void* event, void* pCookie );
static Lockable* CallbackLock;
static int Callback( Upnp_EventType eventType, void* event, void* user_data );
const char* xml_getChildElementValue( IXML_Element* parent, const char* tagName );
IXML_Document* parseBrowseResult( IXML_Document* doc );
const char* xml_getChildElementValue( IXML_Element* parent,
const char* tagName );
IXML_Document* parseBrowseResult( IXML_Document* doc );
// VLC callbacks...
static int Open( vlc_object_t *p_this )
{
int res;
services_discovery_t *p_sd = ( services_discovery_t* )p_this;
services_discovery_sys_t *p_sys = ( services_discovery_sys_t * )
malloc( sizeof( services_discovery_sys_t ) );
playlist_t *p_playlist = pl_Hold( p_sd );
calloc( 1, sizeof( services_discovery_sys_t ) );
p_sd->p_sys = p_sys;
p_sys->p_playlist = p_playlist;
Cookie *cookie = &p_sys->cookie;
/* Create our playlist node */
PL_LOCK;
playlist_NodesPairCreate( p_playlist, _("Devices"),
&p_sys->p_node_cat, &p_sys->p_node_one,
true );
PL_UNLOCK;
cookie->serviceDiscovery = p_sd;
cookie->lock = new Lockable();
cookie->serverList = new MediaServerList( cookie );
services_discovery_SetLocalizedName( p_sd, _("UPnP devices") );
int res = UpnpInit( 0, 0 );
res = UpnpInit( 0, 0 );
if( res != UPNP_E_SUCCESS )
{
msg_Err( p_sd, "%s", UpnpGetErrorMessage( res ) );
goto shutDown;
return VLC_EGENERIC;
}
res = UpnpRegisterClient( Callback, cookie, &cookie->clientHandle );
p_sys->serverList = new MediaServerList( p_sd );
CallbackLock = new Lockable();
res = UpnpRegisterClient( Callback, p_sys, &p_sys->clientHandle );
if( res != UPNP_E_SUCCESS )
{
msg_Err( p_sd, "%s", UpnpGetErrorMessage( res ) );
goto shutDown;
Close( (vlc_object_t*) p_sd );
return VLC_EGENERIC;
}
res = UpnpSearchAsync( cookie->clientHandle, 5, MEDIA_SERVER_DEVICE_TYPE,
cookie );
res = UpnpSearchAsync( p_sys->clientHandle, 5,
MEDIA_SERVER_DEVICE_TYPE, p_sd );
if( res != UPNP_E_SUCCESS )
{
msg_Err( p_sd, "%s", UpnpGetErrorMessage( res ) );
goto shutDown;
Close( (vlc_object_t*) p_sd );
return VLC_EGENERIC;
}
return VLC_SUCCESS;
shutDown:
Close( p_this );
return VLC_EGENERIC;
}
static void Close( vlc_object_t *p_this )
{
services_discovery_t *p_sd = ( services_discovery_t* )p_this;
services_discovery_sys_t *p_sys = p_sd->p_sys;
playlist_t *p_playlist = p_sys->p_playlist;
UpnpFinish();
delete p_sys->cookie.serverList;
delete p_sys->cookie.lock;
delete p_sd->p_sys->serverList;
delete CallbackLock;
PL_LOCK;
playlist_NodeDelete( p_playlist, p_sys->p_node_one, true, true );
playlist_NodeDelete( p_playlist, p_sys->p_node_cat, true, true );
PL_UNLOCK;
pl_Release( p_sd );
free( p_sys );
free( p_sd->p_sys );
}
static void Run( services_discovery_t* p_sd )
{
msg_Dbg( p_sd, "UPnP discovery started" );
while( vlc_object_alive (p_sd) )
{
msleep( 500 );
}
msg_Dbg( p_sd, "UPnP discovery stopped" );
}
// XML utility functions:
// Returns the value of a child element, or 0 on error
const char* xml_getChildElementValue( IXML_Element* parent, const char* tagName )
const char* xml_getChildElementValue( IXML_Element* parent,
const char* tagName )
{
if ( !parent ) return 0;
if ( !tagName ) return 0;
......@@ -388,9 +374,13 @@ const char* xml_getChildElementValue( IXML_Element* parent, const char* tagName
// Extracts the result document from a SOAP response
IXML_Document* parseBrowseResult( IXML_Document* doc )
{
ixmlRelaxParser(1);
if ( !doc ) return 0;
IXML_NodeList* resultList = ixmlDocument_getElementsByTagName( doc, "Result" );
IXML_NodeList* resultList = ixmlDocument_getElementsByTagName( doc,
"Result" );
if ( !resultList ) return 0;
IXML_Node* resultNode = ixmlNodeList_item( resultList, 0 );
......@@ -414,11 +404,12 @@ IXML_Document* parseBrowseResult( IXML_Document* doc )
// Handles all UPnP events
static int Callback( Upnp_EventType eventType, void* event, void* pCookie )
static int Callback( Upnp_EventType eventType, void* event, void* user_data )
{
Cookie* cookie = ( Cookie* )pCookie;
Locker locker( CallbackLock );
Locker locker( cookie->lock );
services_discovery_t *p_sd = ( services_discovery_t* ) user_data;
services_discovery_sys_t* p_sys = p_sd->p_sys;
switch( eventType ) {
......@@ -433,11 +424,14 @@ static int Callback( Upnp_EventType eventType, void* event, void* pCookie )
res = UpnpDownloadXmlDoc( discovery->Location, &descriptionDoc );
if ( res != UPNP_E_SUCCESS )
{
msg_Dbg( cookie->serviceDiscovery, "%s:%d: Could not download device description!", __FILE__, __LINE__ );
return res;
msg_Dbg( p_sd,
"%s:%d: Could not download device description!",
__FILE__, __LINE__ );
return res;
}
MediaServer::parseDeviceDescription( descriptionDoc, discovery->Location, cookie );
MediaServer::parseDeviceDescription( descriptionDoc,
discovery->Location, p_sd );
ixmlDocument_free( descriptionDoc );
}
......@@ -447,7 +441,7 @@ static int Callback( Upnp_EventType eventType, void* event, void* pCookie )
{
struct Upnp_Discovery* discovery = ( struct Upnp_Discovery* )event;
cookie->serverList->removeServer( discovery->DeviceId );
p_sys->serverList->removeServer( discovery->DeviceId );
}
break;
......@@ -455,7 +449,7 @@ static int Callback( Upnp_EventType eventType, void* event, void* pCookie )
{
Upnp_Event* e = ( Upnp_Event* )event;
MediaServer* server = cookie->serverList->getServerBySID( e->Sid );
MediaServer* server = p_sys->serverList->getServerBySID( e->Sid );
if ( server ) server->fetchContents();
}
break;
......@@ -467,21 +461,23 @@ static int Callback( Upnp_EventType eventType, void* event, void* pCookie )
Upnp_Event_Subscribe* s = ( Upnp_Event_Subscribe* )event;
MediaServer* server = cookie->serverList->getServerBySID( s->Sid );
MediaServer* server = p_sys->serverList->getServerBySID( s->Sid );
if ( server ) server->subscribeToContentDirectory();
}
break;
case UPNP_EVENT_SUBSCRIBE_COMPLETE:
msg_Warn( cookie->serviceDiscovery, "subscription complete" );
msg_Warn( p_sd, "subscription complete" );
break;
case UPNP_DISCOVERY_SEARCH_TIMEOUT:
msg_Warn( cookie->serviceDiscovery, "search timeout" );
msg_Warn( p_sd, "search timeout" );
break;
default:
msg_Dbg( cookie->serviceDiscovery, "%s:%d: DEBUG: UNHANDLED EVENT ( TYPE=%d )", __FILE__, __LINE__, eventType );
msg_Dbg( p_sd,
"%s:%d: DEBUG: UNHANDLED EVENT ( TYPE=%d )",
__FILE__, __LINE__, eventType );
break;
}
......@@ -493,129 +489,183 @@ static int Callback( Upnp_EventType eventType, void* event, void* pCookie )
// MediaServer...
void MediaServer::parseDeviceDescription( IXML_Document* doc, const char* location, Cookie* cookie )
void MediaServer::parseDeviceDescription( IXML_Document* doc,
const char* location,
services_discovery_t* p_sd )
{
if ( !doc ) { msg_Dbg( cookie->serviceDiscovery, "%s:%d: NULL", __FILE__, __LINE__ ); return; }
if ( !location ) { msg_Dbg( cookie->serviceDiscovery, "%s:%d: NULL", __FILE__, __LINE__ ); return; }
if ( !doc )
{
msg_Dbg( p_sd, "%s:%d: NULL", __FILE__, __LINE__ );
return;
}
if ( !location )
{
msg_Dbg( p_sd, "%s:%d: NULL", __FILE__, __LINE__ );
return;
}
const char* baseURL = location;
// Try to extract baseURL
IXML_NodeList* urlList = ixmlDocument_getElementsByTagName( doc, "baseURL" );
if ( urlList )
{
if ( IXML_Node* urlNode = ixmlNodeList_item( urlList, 0 ) )
if ( !urlList )
{
IXML_Node* textNode = ixmlNode_getFirstChild( urlNode );
if ( textNode ) baseURL = ixmlNode_getNodeValue( textNode );
}
ixmlNodeList_free( urlList );
if ( IXML_Node* urlNode = ixmlNodeList_item( urlList, 0 ) )
{
IXML_Node* textNode = ixmlNode_getFirstChild( urlNode );
if ( textNode ) baseURL = ixmlNode_getNodeValue( textNode );
}
ixmlNodeList_free( urlList );
}
// Get devices
IXML_NodeList* deviceList = ixmlDocument_getElementsByTagName( doc, "device" );
IXML_NodeList* deviceList =
ixmlDocument_getElementsByTagName( doc, "device" );
if ( deviceList )
{
for ( unsigned int i = 0; i < ixmlNodeList_length( deviceList ); i++ )
{
IXML_Element* deviceElement = ( IXML_Element* )ixmlNodeList_item( deviceList, i );
const char* deviceType = xml_getChildElementValue( deviceElement, "deviceType" );
if ( !deviceType ) { msg_Dbg( cookie->serviceDiscovery, "%s:%d: no deviceType!", __FILE__, __LINE__ ); continue; }
if ( strcmp( MEDIA_SERVER_DEVICE_TYPE, deviceType ) != 0 ) continue;
const char* UDN = xml_getChildElementValue( deviceElement, "UDN" );
if ( !UDN ) { msg_Dbg( cookie->serviceDiscovery, "%s:%d: no UDN!", __FILE__, __LINE__ ); continue; }
if ( cookie->serverList->getServer( UDN ) != 0 ) continue;
const char* friendlyName = xml_getChildElementValue( deviceElement, "friendlyName" );
if ( !friendlyName ) { msg_Dbg( cookie->serviceDiscovery, "%s:%d: no friendlyName!", __FILE__, __LINE__ ); continue; }
MediaServer* server = new MediaServer( UDN, friendlyName, cookie );
if ( !cookie->serverList->addServer( server ) ) {
delete server;
server = 0;
continue;
}
// Check for ContentDirectory service...
IXML_NodeList* serviceList = ixmlElement_getElementsByTagName( deviceElement, "service" );
if ( serviceList )
{
for ( unsigned int j = 0; j < ixmlNodeList_length( serviceList ); j++ )
for ( unsigned int i = 0; i < ixmlNodeList_length( deviceList ); i++ )
{
IXML_Element* serviceElement = ( IXML_Element* )ixmlNodeList_item( serviceList, j );
const char* serviceType = xml_getChildElementValue( serviceElement, "serviceType" );
if ( !serviceType ) continue;
if ( strcmp( CONTENT_DIRECTORY_SERVICE_TYPE, serviceType ) != 0 ) continue;
const char* eventSubURL = xml_getChildElementValue( serviceElement, "eventSubURL" );
if ( !eventSubURL ) continue;
const char* controlURL = xml_getChildElementValue( serviceElement, "controlURL" );
if ( !controlURL ) continue;
IXML_Element* deviceElement =
( IXML_Element* ) ixmlNodeList_item( deviceList, i );
// Try to subscribe to ContentDirectory service
char* url = ( char* )malloc( strlen( baseURL ) + strlen( eventSubURL ) + 1 );
if ( url )
const char* deviceType = xml_getChildElementValue( deviceElement,
"deviceType" );
if ( !deviceType )
{
char* s1 = strdup( baseURL );
char* s2 = strdup( eventSubURL );
if ( UpnpResolveURL( s1, s2, url ) == UPNP_E_SUCCESS )
{
// msg_Dbg( cookie->serviceDiscovery, "CDS EVENT URL: %s", url );
server->setContentDirectoryEventURL( url );
server->subscribeToContentDirectory();
}
free( s1 );
free( s2 );
free( url );
msg_Dbg( p_sd,
"%s:%d: no deviceType!",
__FILE__, __LINE__ );
continue;
}
// Try to browse content directory...
if ( strcmp( MEDIA_SERVER_DEVICE_TYPE, deviceType ) != 0 )
continue;
url = ( char* )malloc( strlen( baseURL ) + strlen( controlURL ) + 1 );
if ( url )
const char* UDN = xml_getChildElementValue( deviceElement, "UDN" );
if ( !UDN )
{
char* s1 = strdup( baseURL );
char* s2 = strdup( controlURL );
if ( UpnpResolveURL( s1, s2, url ) == UPNP_E_SUCCESS )
msg_Dbg( p_sd, "%s:%d: no UDN!",
__FILE__, __LINE__ );
continue;
}
if ( p_sd->p_sys->serverList->getServer( UDN ) != 0 )
continue;
const char* friendlyName =
xml_getChildElementValue( deviceElement,
"friendlyName" );
if ( !friendlyName )
{
// msg_Dbg( cookie->serviceDiscovery, "CDS CTRL URL: %s", url );
server->setContentDirectoryControlURL( url );
server->fetchContents();
msg_Dbg( p_sd, "%s:%d: no friendlyName!", __FILE__, __LINE__ );
continue;
}
free( s1 );
free( s2 );
free( url );
}
}
MediaServer* server = new MediaServer( UDN, friendlyName, p_sd );
if ( !p_sd->p_sys->serverList->addServer( server ) )
{
ixmlNodeList_free( serviceList );
}
}
delete server;
server = 0;
continue;
}
ixmlNodeList_free( deviceList );
// Check for ContentDirectory service...
IXML_NodeList* serviceList =
ixmlElement_getElementsByTagName( deviceElement,
"service" );
if ( serviceList )
{
for ( unsigned int j = 0;
j < ixmlNodeList_length( serviceList ); j++ )
{
IXML_Element* serviceElement =
( IXML_Element* ) ixmlNodeList_item( serviceList, j );
const char* serviceType =
xml_getChildElementValue( serviceElement,
"serviceType" );
if ( !serviceType )
continue;
if ( strcmp( CONTENT_DIRECTORY_SERVICE_TYPE,
serviceType ) != 0 )
continue;
const char* eventSubURL =
xml_getChildElementValue( serviceElement,
"eventSubURL" );
if ( !eventSubURL )
continue;
const char* controlURL =
xml_getChildElementValue( serviceElement,
"controlURL" );
if ( !controlURL )
continue;
// Try to subscribe to ContentDirectory service
char* url = ( char* ) malloc( strlen( baseURL ) +
strlen( eventSubURL ) + 1 );
if ( url )
{
char* s1 = strdup( baseURL );
char* s2 = strdup( eventSubURL );
if ( UpnpResolveURL( s1, s2, url ) ==
UPNP_E_SUCCESS )
{
server->setContentDirectoryEventURL( url );
server->subscribeToContentDirectory();
}
free( s1 );
free( s2 );
free( url );
}
// Try to browse content directory...
url = ( char* ) malloc( strlen( baseURL ) +
strlen( controlURL ) + 1 );
if ( url )
{
char* s1 = strdup( baseURL );
char* s2 = strdup( controlURL );
if ( UpnpResolveURL( s1, s2, url ) ==
UPNP_E_SUCCESS )
{
server->setContentDirectoryControlURL( url );
server->fetchContents();
}
free( s1 );
free( s2 );
free( url );
}
}
ixmlNodeList_free( serviceList );
}
}
ixmlNodeList_free( deviceList );
}
}
MediaServer::MediaServer( const char* UDN, const char* friendlyName, Cookie* cookie )
MediaServer::MediaServer( const char* UDN,
const char* friendlyName,
services_discovery_t* p_sd )
{
_cookie = cookie;
_p_sd = p_sd;
_UDN = UDN;
_friendlyName = friendlyName;
......@@ -626,14 +676,6 @@ MediaServer::MediaServer( const char* UDN, const char* friendlyName, Cookie* coo
MediaServer::~MediaServer()
{
if ( _contents )
{
vlc_object_lock( _cookie->serviceDiscovery->p_sys->p_playlist );
playlist_NodeDelete( pl_Get( _cookie->serviceDiscovery ) ,
_playlistNode, true, true );
vlc_object_unlock( _cookie->serviceDiscovery->p_sys->p_playlist );
}
delete _contents;
}
......@@ -675,34 +717,44 @@ void MediaServer::subscribeToContentDirectory()
const char* url = getContentDirectoryEventURL();
if ( !url || strcmp( url, "" ) == 0 )
{
msg_Dbg( _cookie->serviceDiscovery, "No subscription url set!" );
return;
msg_Dbg( _p_sd, "No subscription url set!" );
return;
}
int timeOut = 1810;
Upnp_SID sid;
int res = UpnpSubscribe( _cookie->clientHandle, url, &timeOut, sid );
int res = UpnpSubscribe( _p_sd->p_sys->clientHandle, url, &timeOut, sid );