📄 upnp_intel.cpp
字号:
/***************************************************************************** * Upnp_intel.cpp : UPnP discovery module (Intel SDK) ***************************************************************************** * Copyright (C) 2004-2008 the VideoLAN team * $Id: 0c3f3a59a797bf7ccc34b882bfdbd85789c908ae $ * * Authors: Rémi Denis-Courmont <rem # videolan.org> (original plugin) * Christian Henz <henz # c-lab.de> * * UPnP Plugin using the Intel SDK (libupnp) instead of CyberLink * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************//* \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 <vlc_plugin.h>#include <vlc_playlist.h>// VLC handlestruct services_discovery_sys_t{ playlist_t *p_playlist; playlist_item_t *p_node_cat; playlist_item_t *p_node_one;};// Constantsconst 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";// Classesclass MediaServer;class MediaServerList;class Item;class Container;// Cookie that is passed to the callbacktypedef struct{ services_discovery_t* serviceDiscovery; UpnpClient_Handle clientHandle; MediaServerList* serverList;} Cookie;// Class definitions...class Lockable{public: Lockable( Cookie* c ) { 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, Cookie* cookie ); MediaServer( const char* UDN, const char* friendlyName, Cookie* cookie ); ~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* ); Cookie* _cookie; 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( Cookie* cookie ); ~MediaServerList(); bool addServer( MediaServer* s ); void removeServer( const char* UDN ); MediaServer* getServer( const char* UDN ); MediaServer* getServerBySID( const char* );private: Cookie* _cookie; 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 prototypesstatic int Open( vlc_object_t* );static void Close( vlc_object_t* );static void Run( services_discovery_t *p_sd );static playlist_t *pl_Get( services_discovery_t *p_sd ){ return p_sd->p_sys->p_playlist;}// Module descriptorvlc_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 Lockable* CallbackLock;static int Callback( Upnp_EventType eventType, void* event, void* pCookie );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 ){ 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 ) ); p_sd->pf_run = Run; p_sd->p_sys = p_sys; p_sys->p_playlist = pl_Yield( p_sd ); /* Create our playlist node */ vlc_object_lock( p_sys->p_playlist ); playlist_NodesPairCreate( pl_Get( p_sd ), _("Devices"), &p_sys->p_node_cat, &p_sys->p_node_one, true ); vlc_object_unlock( p_sys->p_playlist ); return VLC_SUCCESS;}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; vlc_object_lock( p_sys->p_playlist ); playlist_NodeDelete( pl_Get( p_sd ), p_sys->p_node_one, true, true ); playlist_NodeDelete( pl_Get( p_sd ), p_sys->p_node_cat, true, true ); vlc_object_unlock( p_sys->p_playlist ); pl_Release( p_sd ); free( p_sys );}static void Run( services_discovery_t* p_sd ){ int res; res = UpnpInit( 0, 0 ); if( res != UPNP_E_SUCCESS ) { msg_Err( p_sd, "%s", UpnpGetErrorMessage( res ) ); return; } Cookie cookie; cookie.serviceDiscovery = p_sd; cookie.serverList = new MediaServerList( &cookie ); CallbackLock = new Lockable( &cookie ); res = UpnpRegisterClient( Callback, &cookie, &cookie.clientHandle ); if( res != UPNP_E_SUCCESS ) { msg_Err( p_sd, "%s", UpnpGetErrorMessage( res ) ); goto shutDown; } res = UpnpSearchAsync( cookie.clientHandle, 5, MEDIA_SERVER_DEVICE_TYPE, &cookie ); if( res != UPNP_E_SUCCESS ) { msg_Err( p_sd, "%s", UpnpGetErrorMessage( res ) ); goto shutDown; } msg_Dbg( p_sd, "UPnP discovery started" ); while( vlc_object_alive (p_sd) ) { msleep( 500 ); } msg_Dbg( p_sd, "UPnP discovery stopped" ); shutDown: UpnpFinish(); delete cookie.serverList; delete CallbackLock;}// XML utility functions:// Returns the value of a child element, or 0 on errorconst char* xml_getChildElementValue( IXML_Element* parent, const char* tagName ){ if ( !parent ) return 0; if ( !tagName ) return 0; char* s = strdup( tagName ); IXML_NodeList* nodeList = ixmlElement_getElementsByTagName( parent, s ); free( s ); if ( !nodeList ) return 0; IXML_Node* element = ixmlNodeList_item( nodeList, 0 ); ixmlNodeList_free( nodeList ); if ( !element ) return 0; IXML_Node* textNode = ixmlNode_getFirstChild( element ); if ( !textNode ) return 0; return ixmlNode_getNodeValue( textNode );}// Extracts the result document from a SOAP responseIXML_Document* parseBrowseResult( IXML_Document* doc ){ if ( !doc ) return 0; IXML_NodeList* resultList = ixmlDocument_getElementsByTagName( doc, "Result" ); if ( !resultList ) return 0; IXML_Node* resultNode = ixmlNodeList_item( resultList, 0 ); ixmlNodeList_free( resultList ); if ( !resultNode ) return 0; IXML_Node* textNode = ixmlNode_getFirstChild( resultNode ); if ( !textNode ) return 0; const char* resultString = ixmlNode_getNodeValue( textNode ); char* resultXML = strdup( resultString ); IXML_Document* browseDoc = ixmlParseBuffer( resultXML ); free( resultXML ); return browseDoc;}// Handles all UPnP eventsstatic int Callback( Upnp_EventType eventType, void* event, void* pCookie ){ Locker locker( CallbackLock ); Cookie* cookie = ( Cookie* )pCookie; switch( eventType ) { case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE: case UPNP_DISCOVERY_SEARCH_RESULT: { struct Upnp_Discovery* discovery = ( struct Upnp_Discovery* )event; IXML_Document *descriptionDoc = 0; int res; 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; } MediaServer::parseDeviceDescription( descriptionDoc, discovery->Location, cookie ); ixmlDocument_free( descriptionDoc ); } break; case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE: { struct Upnp_Discovery* discovery = ( struct Upnp_Discovery* )event; cookie->serverList->removeServer( discovery->DeviceId ); } break; case UPNP_EVENT_RECEIVED: { Upnp_Event* e = ( Upnp_Event* )event; MediaServer* server = cookie->serverList->getServerBySID( e->Sid ); if ( server ) server->fetchContents(); } break; case UPNP_EVENT_AUTORENEWAL_FAILED: case UPNP_EVENT_SUBSCRIPTION_EXPIRED: { // Re-subscribe... Upnp_Event_Subscribe* s = ( Upnp_Event_Subscribe* )event; MediaServer* server = cookie->serverList->getServerBySID( s->Sid ); if ( server ) server->subscribeToContentDirectory(); } break; case UPNP_EVENT_SUBSCRIBE_COMPLETE: msg_Warn( cookie->serviceDiscovery, "subscription complete" ); break; case UPNP_DISCOVERY_SEARCH_TIMEOUT: msg_Warn( cookie->serviceDiscovery, "search timeout" ); break; default: msg_Dbg( cookie->serviceDiscovery, "%s:%d: DEBUG: UNHANDLED EVENT ( TYPE=%d )", __FILE__, __LINE__, eventType ); break; } return UPNP_E_SUCCESS;}// Class implementations...// MediaServer...void MediaServer::parseDeviceDescription( IXML_Document* doc, const char* location, Cookie* cookie ){ if ( !doc ) { msg_Dbg( cookie->serviceDiscovery, "%s:%d: NULL", __FILE__, __LINE__ ); return; } if ( !location ) { msg_Dbg( cookie->serviceDiscovery, "%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 ) ) { IXML_Node* textNode = ixmlNode_getFirstChild( urlNode ); if ( textNode ) baseURL = ixmlNode_getNodeValue( textNode ); } ixmlNodeList_free( urlList ); } // Get devices 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++ ) { IXML_Element* serviceElement = ( IXML_Element* )ixmlNodeList_item( serviceList, j );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -