1 /*****************************************************************************
2 * upnp.cpp : UPnP discovery module
3 *****************************************************************************
4 * Copyright (C) 2004-2005 the VideoLAN team
5 * $Id: sap.c 11664 2005-07-09 06:17:09Z courmisch $
7 * Authors: RĂ©mi Denis-Courmont <rem # videolan.org>
9 * Based on original wxWindows patch for VLC, and dependant on CyberLink
11 * Satoshi Konno <skonno@cybergarage.org>
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
26 *****************************************************************************/
28 /*****************************************************************************
30 *****************************************************************************/
31 #include <stdlib.h> /* malloc(), free() */
33 #include <cybergarage/upnp/media/player/MediaPlayer.h>
39 /* FIXME: thread-safety ?? */
40 /* FIXME: playlist locking */
42 /************************************************************************
43 * Macros and definitions
44 ************************************************************************/
46 using namespace CyberLink;
48 /*****************************************************************************
50 *****************************************************************************/
53 static int Open ( vlc_object_t * );
54 static void Close( vlc_object_t * );
57 set_shortname( _("UPnP"));
58 set_description( _("Universal Plug'n'Play discovery") );
59 set_category( CAT_PLAYLIST );
60 set_subcategory( SUBCAT_PLAYLIST_SD );
62 set_capability( "services_discovery", 0 );
63 set_callbacks( Open, Close );
68 /*****************************************************************************
70 *****************************************************************************/
72 struct services_discovery_sys_t
75 playlist_item_t *p_node;
76 playlist_t *p_playlist;
79 /*****************************************************************************
81 *****************************************************************************/
84 static void Run ( services_discovery_t *p_sd );
86 /*****************************************************************************
87 * Open: initialize and create stuff
88 *****************************************************************************/
89 static int Open( vlc_object_t *p_this )
91 services_discovery_t *p_sd = ( services_discovery_t* )p_this;
92 services_discovery_sys_t *p_sys = (services_discovery_sys_t *)
93 malloc( sizeof( services_discovery_sys_t ) );
95 playlist_view_t *p_view;
101 /* Create our playlist node */
102 p_sys->p_playlist = (playlist_t *)vlc_object_find( p_sd,
105 if( !p_sys->p_playlist )
107 msg_Warn( p_sd, "unable to find playlist, cancelling UPnP listening");
111 p_view = playlist_ViewFind( p_sys->p_playlist, VIEW_CATEGORY );
112 p_sys->p_node = playlist_NodeCreate( p_sys->p_playlist, VIEW_CATEGORY,
113 _("UPnP"), p_view->p_root );
114 p_sys->p_node->i_flags |= PLAYLIST_RO_FLAG;
115 p_sys->p_node->i_flags &= ~PLAYLIST_SKIP_FLAG;
116 val.b_bool = VLC_TRUE;
117 var_Set( p_sys->p_playlist, "intf-change", val );
123 /*****************************************************************************
125 *****************************************************************************/
126 static void Close( vlc_object_t *p_this )
128 services_discovery_t *p_sd = ( services_discovery_t* )p_this;
129 services_discovery_sys_t *p_sys = p_sd->p_sys;
131 if( p_sys->p_playlist )
133 playlist_NodeDelete( p_sys->p_playlist, p_sys->p_node, VLC_TRUE,
135 vlc_object_release( p_sys->p_playlist );
141 /*****************************************************************************
142 * Run: main UPnP thread
143 *****************************************************************************
144 * Processes UPnP events
145 *****************************************************************************/
146 class UPnPHandler : public MediaPlayer, public DeviceChangeListener,
147 /*public EventListener,*/ public SearchResponseListener
150 services_discovery_t *p_sd;
151 services_discovery_sys_t *p_sys;
153 Device *GetDeviceFromUSN( const string& usn )
155 return getDevice( usn.substr( 0, usn.find( "::" ) ).c_str() );
158 playlist_item_t *FindDeviceNode( Device *dev )
160 return playlist_ChildSearchName( p_sys->p_node, dev->getFriendlyName() );
163 playlist_item_t *FindDeviceNode( const string &usn )
165 return FindDeviceNode( GetDeviceFromUSN( usn ) );
168 playlist_item_t *AddDevice( Device *dev );
169 void AddDeviceContent( Device *dev );
170 void AddContent( playlist_item_t *p_parent, ContentNode *node );
171 void RemoveDevice( Device *dev );
173 /* CyberLink callbacks */
174 virtual void deviceAdded( Device *dev );
175 virtual void deviceRemoved( Device *dev );
177 virtual void deviceSearchResponseReceived( SSDPPacket *packet );
178 /*virtual void eventNotifyReceived( const char *uuid, long seq,
180 const char *value );*/
183 UPnPHandler( services_discovery_t *p_this )
184 : p_sd( p_this ), p_sys( p_this->p_sys )
186 addDeviceChangeListener( this );
187 addSearchResponseListener( this );
188 //addEventListener( this );
193 static void Run( services_discovery_t *p_sd )
195 UPnPHandler u( p_sd );
199 msg_Dbg( p_sd, "UPnP discovery started" );
200 /* read SAP packets */
201 while( !p_sd->b_die )
207 msg_Dbg( p_sd, "UPnP discovery stopped" );
211 playlist_item_t *UPnPHandler::AddDevice( Device *dev )
216 /* We are not interested in IGD devices or whatever (at the moment) */
217 if ( !dev->isDeviceType( MediaServer::DEVICE_TYPE ) )
220 playlist_item_t *p_item = FindDeviceNode( dev );
221 if ( p_item != NULL )
225 * Maybe one day, VLC API will make sensible use of the const keyword;
226 * That day, you will no longer need this strdup().
228 char *str = strdup( dev->getFriendlyName( ) );
230 p_item = playlist_NodeCreate( p_sys->p_playlist, VIEW_CATEGORY,
231 str, p_sys->p_node );
232 p_item->i_flags &= ~PLAYLIST_SKIP_FLAG;
233 msg_Dbg( p_sd, "device %s added", str );
239 void UPnPHandler::AddDeviceContent( Device *dev )
241 playlist_item_t *p_devnode = AddDevice( dev );
243 if( p_devnode == NULL )
246 AddContent( p_devnode, getContentDirectory( dev ) );
249 void UPnPHandler::AddContent( playlist_item_t *p_parent, ContentNode *node )
254 const char *title = node->getTitle();
258 msg_Dbg( p_sd, "title = %s", title );
260 if( !node->isContainerNode() )
262 msg_Dbg( p_sd, "not a container" );
266 ContainerNode *conNode = (ContainerNode *)node;
267 ItemNode *iNode = (ItemNode *)node;
269 playlist_item_t *p_item;
270 p_item = playlist_ItemNew( p_sd, iNode->getResource(), title );
271 playlist_NodeAddItem( p_sys->p_playlist, p_item, VIEW_CATEGORY,
272 p_parent, PLAYLIST_APPEND, PLAYLIST_END );
274 /*if( !cnode->hasContainerNodes() )
277 unsigned nContentNodes = conNode->getNContentNodes();
279 for( unsigned n = 0; n < nContentNodes; n++ )
280 AddContent( p_item, conNode->getContentNode( n ) );
284 void UPnPHandler::RemoveDevice( Device *dev )
286 playlist_item_t *p_item = FindDeviceNode( dev );
289 playlist_NodeDelete( p_sys->p_playlist, p_item, VLC_TRUE, VLC_TRUE );
293 void UPnPHandler::deviceAdded( Device *dev )
295 msg_Dbg( p_sd, "adding device" );
296 AddDeviceContent( dev );
300 void UPnPHandler::deviceRemoved( Device *dev )
302 msg_Dbg( p_sd, "removing device" );
307 void UPnPHandler::deviceSearchResponseReceived( SSDPPacket *packet )
309 if( !packet->isRootDevice() )
312 string usn, nts, nt, udn;
314 packet->getUSN( usn );
316 packet->getNTS( nts );
317 udn = usn.substr( 0, usn.find( "::" ) );
319 Device *dev = GetDeviceFromUSN( usn );
320 if( packet->isByeBye() )
323 AddDeviceContent( dev );
326 /*void UPnPHandler::eventNotifyReceived( const char *uuid, long seq,
327 const char *name, const char *value )
329 msg_Dbg( p_sd, "event notify received" );
330 msg_Dbg( p_sd, "uuid = %s, name = %s, value = %s", uuid, name, value );