/*
\TODO: Debug messages: "__FILE__, __LINE__" ok ???, Wrn/Err ???
\TODO: Change names to VLC standard ???
- \TODO: Rewrite this using the new service discovery API (see sap.c, shout.c).
*/
-
-
-#include <vector>
-#include <string>
-
-#include <upnp/upnp.h>
-#include <upnp/upnptools.h>
-
#undef PACKAGE_NAME
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
-#include <vlc_common.h>
+#include "upnp_intel.hpp"
+
#include <vlc_plugin.h>
-#include <vlc_playlist.h>
+#include <vlc_services_discovery.h>
-// Constants
+// Constants
const char* MEDIA_SERVER_DEVICE_TYPE = "urn:schemas-upnp-org:device:MediaServer:1";
const char* CONTENT_DIRECTORY_SERVICE_TYPE = "urn:schemas-upnp-org:service:ContentDirectory:1";
-
-// Classes
-
-class MediaServer;
-class MediaServerList;
-class Item;
-class Container;
-
// VLC handle
-
struct services_discovery_sys_t
{
UpnpClient_Handle clientHandle;
MediaServerList* serverList;
+ vlc_mutex_t callbackLock;
};
-// Class definitions...
-
-class Lockable
-{
-public:
-
- Lockable()
- {
- vlc_mutex_init( &_mutex );
- }
-
- ~Lockable()
- {
- vlc_mutex_destroy( &_mutex );
- }
-
- void lock() { vlc_mutex_lock( &_mutex ); }
- void unlock() { vlc_mutex_unlock( &_mutex ); }
-
-private:
-
- vlc_mutex_t _mutex;
-};
-
-
-class Locker
-{
-public:
- Locker( Lockable* l )
- {
- _lockable = l;
- _lockable->lock();
- }
-
- ~Locker()
- {
- _lockable->unlock();
- }
-
-private:
- Lockable* _lockable;
-};
-
-
-class MediaServer
-{
-public:
-
- static void parseDeviceDescription( IXML_Document* doc,
- const char* location,
- services_discovery_t* p_sd );
-
- MediaServer( const char* UDN,
- const char* friendlyName,
- services_discovery_t* p_sd );
-
- ~MediaServer();
-
- const char* getUDN() const;
- const char* getFriendlyName() const;
-
- void setContentDirectoryEventURL( const char* url );
- const char* getContentDirectoryEventURL() const;
-
- void setContentDirectoryControlURL( const char* url );
- const char* getContentDirectoryControlURL() const;
-
- void subscribeToContentDirectory();
- void fetchContents();
-
- void setPlaylistNode( playlist_item_t* node );
-
- bool compareSID( const char* sid );
-
-private:
-
- bool _fetchContents( Container* parent );
- void _buildPlaylist( Container* container );
-
- IXML_Document* _browseAction( const char*, const char*,
- const char*, const char*, const char*, const char* );
-
- services_discovery_t* _p_sd;
-
- Container* _contents;
- playlist_item_t* _playlistNode;
-
- std::string _UDN;
- std::string _friendlyName;
-
- std::string _contentDirectoryEventURL;
- std::string _contentDirectoryControlURL;
-
- int _subscriptionTimeOut;
- Upnp_SID _subscriptionID;
-};
-
-
-class MediaServerList
-{
-public:
-
- MediaServerList( services_discovery_t* p_sd );
- ~MediaServerList();
-
- bool addServer( MediaServer* s );
- void removeServer( const char* UDN );
-
- MediaServer* getServer( const char* UDN );
- MediaServer* getServerBySID( const char* );
-
-private:
-
- services_discovery_t* _p_sd;
-
- std::vector<MediaServer*> _list;
-};
-
-
-class Item
-{
-public:
-
- Item( Container* parent,
- const char* objectID,
- const char* title,
- const char* resource );
-
- const char* getObjectID() const;
- const char* getTitle() const;
- const char* getResource() const;
-
- void setPlaylistNode( playlist_item_t* node );
- playlist_item_t* getPlaylistNode() const ;
-
-private:
-
- playlist_item_t* _playlistNode;
-
- Container* _parent;
- std::string _objectID;
- std::string _title;
- std::string _resource;
-};
-
-
-class Container
-{
-public:
-
- Container( Container* parent, const char* objectID, const char* title );
- ~Container();
-
- void addItem( Item* item );
- void addContainer( Container* container );
-
- const char* getObjectID() const;
- const char* getTitle() const;
-
- unsigned int getNumItems() const;
- unsigned int getNumContainers() const;
-
- Item* getItem( unsigned int i ) const;
- Container* getContainer( unsigned int i ) const;
-
- void setPlaylistNode( playlist_item_t* node );
- playlist_item_t* getPlaylistNode() const;
-
-private:
-
- playlist_item_t* _playlistNode;
-
- Container* _parent;
-
- std::string _objectID;
- std::string _title;
- std::vector<Item*> _items;
- std::vector<Container*> _containers;
-};
-
-
// VLC callback prototypes
-
static int Open( vlc_object_t* );
static void Close( vlc_object_t* );
-static void Run( services_discovery_t *p_sd );
+VLC_SD_PROBE_HELPER("upnp", N_("Universal Plug'n'Play"), SD_CAT_LAN)
// 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 );
+ set_shortname( "UPnP" );
+ set_description( N_( "Universal Plug'n'Play" ) );
+ set_category( CAT_PLAYLIST );
+ set_subcategory( SUBCAT_PLAYLIST_SD );
+ set_capability( "services_discovery", 0 );
+ set_callbacks( Open, Close );
+
+ VLC_SD_PROBE_SUBMODULE
vlc_module_end();
// More prototypes...
-static Lockable* CallbackLock;
static int Callback( Upnp_EventType eventType, void* event, void* user_data );
const char* xml_getChildElementValue( IXML_Element* parent,
services_discovery_sys_t *p_sys = ( services_discovery_sys_t * )
calloc( 1, sizeof( services_discovery_sys_t ) );
- p_sd->p_sys = p_sys;
-
- services_discovery_SetLocalizedName( p_sd, _("UPnP devices") );
+ if(!(p_sd->p_sys = p_sys))
+ return VLC_ENOMEM;
res = UpnpInit( 0, 0 );
if( res != UPNP_E_SUCCESS )
{
msg_Err( p_sd, "%s", UpnpGetErrorMessage( res ) );
+ free( p_sys );
return VLC_EGENERIC;
}
p_sys->serverList = new MediaServerList( p_sd );
- CallbackLock = new Lockable();
+ vlc_mutex_init( &p_sys->callbackLock );
- res = UpnpRegisterClient( Callback, p_sys, &p_sys->clientHandle );
+ res = UpnpRegisterClient( Callback, p_sd, &p_sys->clientHandle );
if( res != UPNP_E_SUCCESS )
{
msg_Err( p_sd, "%s", UpnpGetErrorMessage( res ) );
res = UpnpSearchAsync( p_sys->clientHandle, 5,
MEDIA_SERVER_DEVICE_TYPE, p_sd );
-
+
if( res != UPNP_E_SUCCESS )
{
msg_Err( p_sd, "%s", UpnpGetErrorMessage( res ) );
UpnpFinish();
delete p_sd->p_sys->serverList;
- delete CallbackLock;
+ vlc_mutex_destroy( &p_sd->p_sys->callbackLock );
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
// Handles all UPnP events
static int Callback( Upnp_EventType eventType, void* event, void* user_data )
{
- Locker locker( CallbackLock );
-
services_discovery_t *p_sd = ( services_discovery_t* ) user_data;
services_discovery_sys_t* p_sys = p_sd->p_sys;
+ vlc_mutex_locker locker( &p_sys->callbackLock );
switch( eventType ) {
case UPNP_EVENT_SUBSCRIBE_COMPLETE:
msg_Warn( p_sd, "subscription complete" );
break;
-
+
case UPNP_DISCOVERY_SEARCH_TIMEOUT:
msg_Warn( p_sd, "search timeout" );
break;
-
+
default:
msg_Dbg( p_sd,
"%s:%d: DEBUG: UNHANDLED EVENT ( TYPE=%d )",
services_discovery_t* p_sd )
{
if ( !doc )
- {
- msg_Dbg( p_sd, "%s:%d: NULL", __FILE__, __LINE__ );
+ {
+ msg_Dbg( p_sd, "%s:%d: NULL", __FILE__, __LINE__ );
return;
}
-
+
if ( !location )
{
- msg_Dbg( p_sd, "%s:%d: NULL", __FILE__, __LINE__ );
+ msg_Dbg( p_sd, "%s:%d: NULL", __FILE__, __LINE__ );
return;
}
IXML_NodeList* deviceList =
ixmlDocument_getElementsByTagName( doc, "device" );
-
+
if ( deviceList )
{
for ( unsigned int i = 0; i < ixmlNodeList_length( deviceList ); i++ )
__FILE__, __LINE__ );
continue;
}
-
+
if ( p_sd->p_sys->serverList->getServer( UDN ) != 0 )
continue;
const char* friendlyName =
- xml_getChildElementValue( deviceElement,
+ xml_getChildElementValue( deviceElement,
"friendlyName" );
-
+
if ( !friendlyName )
{
msg_Dbg( p_sd, "%s:%d: no friendlyName!", __FILE__, __LINE__ );
}
MediaServer* server = new MediaServer( UDN, friendlyName, p_sd );
-
+
if ( !p_sd->p_sys->serverList->addServer( server ) )
{
_UDN = UDN;
_friendlyName = friendlyName;
- _contents = 0;
- _playlistNode = 0;
+ _contents = NULL;
+ _inputItem = NULL;
}
MediaServer::~MediaServer()
IXML_Document* action = 0;
IXML_Document* response = 0;
const char* url = getContentDirectoryControlURL();
-
+
if ( !url || strcmp( url, "" ) == 0 )
{
msg_Dbg( _p_sd, "No subscription url set!" );
res = UpnpAddToAction( &action, "Browse",
serviceType, "ObjectID", ObjectID );
-
- if ( res != UPNP_E_SUCCESS )
+
+ if ( res != UPNP_E_SUCCESS )
{
msg_Dbg( _p_sd,
"%s:%d: ERROR: %s", __FILE__, __LINE__,
res = UpnpAddToAction( &action, "Browse",
serviceType, "BrowseFlag", BrowseFlag );
-
+
if ( res != UPNP_E_SUCCESS )
{
msg_Dbg( _p_sd,
res = UpnpAddToAction( &action, "Browse",
serviceType, "Filter", Filter );
-
+
if ( res != UPNP_E_SUCCESS )
{
msg_Dbg( _p_sd,
res = UpnpAddToAction( &action, "Browse",
serviceType, "SortCriteria", SortCriteria );
-
+
if ( res != UPNP_E_SUCCESS )
{
msg_Dbg( _p_sd,
0,
action,
&response );
-
+
if ( res != UPNP_E_SUCCESS )
{
msg_Dbg( _p_sd,
void MediaServer::fetchContents()
{
Container* root = new Container( 0, "0", getFriendlyName() );
- playlist_t * p_playlist = pl_Hold( _p_sd );
_fetchContents( root );
- if ( _contents )
- {
- PL_LOCK;
- playlist_NodeEmpty( p_playlist, _playlistNode, true );
- PL_UNLOCK;
- delete _contents;
- }
-
_contents = root;
- _contents->setPlaylistNode( _playlistNode );
+ _contents->setInputItem( _inputItem );
- _buildPlaylist( _contents );
- pl_Release ( _p_sd );
+ _buildPlaylist( _contents, NULL );
}
bool MediaServer::_fetchContents( Container* parent )
IXML_Document* result = parseBrowseResult( response );
ixmlDocument_free( response );
-
+
if ( !result )
{
msg_Dbg( _p_sd,
IXML_NodeList* containerNodeList =
ixmlDocument_getElementsByTagName( result, "container" );
-
+
if ( containerNodeList )
{
for ( unsigned int i = 0;
const char* childCountStr =
ixmlElement_getAttribute( containerElement, "childCount" );
-
+
if ( !childCountStr )
continue;
-
+
int childCount = atoi( childCountStr );
const char* title = xml_getChildElementValue( containerElement,
"dc:title" );
-
+
if ( !title )
continue;
-
+
const char* resource = xml_getChildElementValue( containerElement,
"res" );
const char* objectID =
ixmlElement_getAttribute( itemElement, "id" );
-
+
if ( !objectID )
continue;
const char* title =
xml_getChildElementValue( itemElement, "dc:title" );
-
+
if ( !title )
continue;
const char* resource =
xml_getChildElementValue( itemElement, "res" );
-
+
if ( !resource )
continue;
return true;
}
-void MediaServer::_buildPlaylist( Container* parent )
+void MediaServer::_buildPlaylist( Container* parent, input_item_node_t *p_input_node )
{
- playlist_t *p_playlist = pl_Hold( _p_sd );
+ bool send = p_input_node == NULL;
+ if( send )
+ p_input_node = input_item_node_Create( parent->getInputItem() );
+
for ( unsigned int i = 0; i < parent->getNumContainers(); i++ )
{
Container* container = parent->getContainer( i );
- playlist_item_t* parentNode = parent->getPlaylistNode();
- char* title = strdup( container->getTitle() );
- PL_LOCK;
- playlist_item_t* node = playlist_NodeCreate( p_playlist,
- title, parentNode, 0, NULL );
- PL_UNLOCK;
- free( title );
+ input_item_t* p_input_item = input_item_New( _p_sd, "vlc://nop", parent->getTitle() );
+ input_item_node_t *p_new_node =
+ input_item_node_AppendItem( p_input_node, p_input_item );
- container->setPlaylistNode( node );
- _buildPlaylist( container );
+ container->setInputItem( p_input_item );
+ _buildPlaylist( container, p_new_node );
}
for ( unsigned int i = 0; i < parent->getNumItems(); i++ )
{
Item* item = parent->getItem( i );
- playlist_item_t* parentNode = parent->getPlaylistNode();
- input_item_t* p_input = input_item_New( _p_sd,
+ input_item_t* p_input_item = input_item_New( _p_sd,
item->getResource(),
item->getTitle() );
- int i_cat;
- /* FIXME: playlist_AddInput() can fail */
- playlist_BothAddInput( p_playlist, p_input, parentNode,
- PLAYLIST_APPEND, PLAYLIST_END, &i_cat, NULL,
- pl_Unlocked );
- vlc_gc_decref( p_input );
- /* TODO: do this better by storing ids */
- playlist_item_t *p_node =
- playlist_ItemGetById( p_playlist, i_cat, false );
- assert( p_node );
- item->setPlaylistNode( p_node );
+ assert( p_input_item );
+ input_item_node_AppendItem( p_input_node, p_input_item );
+ item->setInputItem( p_input_item );
}
- pl_Release( _p_sd );
+
+ if( send )
+ input_item_node_PostAndDelete( p_input_node );
}
-void MediaServer::setPlaylistNode( playlist_item_t* playlistNode )
+void MediaServer::setInputItem( input_item_t* p_input_item )
{
- _playlistNode = playlistNode;
+ if(_inputItem == p_input_item)
+ return;
+
+ if(_inputItem)
+ vlc_gc_decref( _inputItem );
+
+ vlc_gc_incref( p_input_item );
+ _inputItem = p_input_item;
}
bool MediaServer::compareSID( const char* sid )
bool MediaServerList::addServer( MediaServer* s )
{
+ input_item_t* p_input_item = NULL;
if ( getServer( s->getUDN() ) != 0 ) return false;
msg_Dbg( _p_sd, "Adding server '%s'",
s->getFriendlyName() );
services_discovery_t* p_sd = _p_sd;
- services_discovery_sys_t* p_sys = p_sd->p_sys;
- playlist_item_t *p_node_cat;
- playlist_item_t *p_node_one;
- playlist_t* p_playlist = pl_Hold( _p_sd );
+ p_input_item = input_item_New( p_sd, "vlc://nop", s->getFriendlyName() );
+ s->setInputItem( p_input_item );
- for(int i = 0; i < p_playlist->i_sds; i++ )
- {
- if(p_playlist->pp_sds[i]->p_sd == p_sd )
- {
- p_node_cat = p_playlist->pp_sds[i]->p_cat;
- p_node_one = p_playlist->pp_sds[i]->p_one;
- break;
- }
- }
-
- assert (p_node_cat);
- assert (p_node_one);
+ services_discovery_AddItem( p_sd, p_input_item, NULL );
_list.push_back( s );
-
- char* name = strdup( s->getFriendlyName() );
- PL_LOCK;
- playlist_item_t* node =
- playlist_NodeCreate( p_playlist, name, p_node_cat, 0, NULL );
- PL_UNLOCK;
- pl_Release( _p_sd );
- free( name );
- s->setPlaylistNode( node );
return true;
}
_title = title;
_resource = resource;
- _playlistNode = 0;
+ _inputItem = NULL;
+}
+
+Item::~Item()
+{
+ if(_inputItem)
+ vlc_gc_decref( _inputItem );
}
const char* Item::getObjectID() const
return _resource.c_str();
}
-void Item::setPlaylistNode( playlist_item_t* node )
+void Item::setInputItem( input_item_t* p_input_item )
{
- _playlistNode = node;
+ if(_inputItem == p_input_item)
+ return;
+
+ if(_inputItem)
+ vlc_gc_decref( _inputItem );
+
+ vlc_gc_incref( p_input_item );
+ _inputItem = p_input_item;
}
-playlist_item_t* Item::getPlaylistNode() const
+input_item_t* Item::getInputItem() const
{
- return _playlistNode;
+ return _inputItem;
}
_objectID = objectID;
_title = title;
- _playlistNode = 0;
+ _inputItem = NULL;
}
Container::~Container()
{
delete _items[i];
}
+
+ if(_inputItem )
+ vlc_gc_decref( _inputItem );
}
void Container::addItem( Item* item )
return 0;
}
-void Container::setPlaylistNode( playlist_item_t* node )
+Container* Container::getParent()
{
- _playlistNode = node;
+ return _parent;
+}
+
+void Container::setInputItem( input_item_t* p_input_item )
+{
+ if(_inputItem == p_input_item)
+ return;
+
+ if(_inputItem)
+ vlc_gc_decref( _inputItem );
+
+ vlc_gc_incref( p_input_item );
+ _inputItem = p_input_item;
}
-playlist_item_t* Container::getPlaylistNode() const
+input_item_t* Container::getInputItem() const
{
- return _playlistNode;
+ return _inputItem;
}