]> git.sesse.net Git - vlc/blob - modules/services_discovery/upnp.cpp
* modules/video_output/x11/xcommon.c: support on-the-fly ar/cropping in the x11 vout.
[vlc] / modules / services_discovery / upnp.cpp
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 $
6  *
7  * Authors: RĂ©mi Denis-Courmont <rem # videolan.org>
8  * 
9  * Based on original wxWindows patch for VLC, and dependant on CyberLink
10  * UPnP library from :
11  *          Satoshi Konno <skonno@cybergarage.org>
12  *
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.
17  *
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.
22  *
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  *****************************************************************************/
27
28 /*****************************************************************************
29  * Includes
30  *****************************************************************************/
31 #include <stdlib.h>                                      /* malloc(), free() */
32
33 #include <cybergarage/upnp/media/player/MediaPlayer.h>
34
35 #undef PACKAGE_NAME
36 #include <vlc/vlc.h>
37 #include <vlc/intf.h>
38
39 /* FIXME: thread-safety ?? */
40 /* FIXME: playlist locking */
41
42 /************************************************************************
43  * Macros and definitions
44  ************************************************************************/
45 using namespace std;
46 using namespace CyberLink;
47
48 /*****************************************************************************
49  * Module descriptor
50  *****************************************************************************/
51
52 /* Callbacks */
53     static int  Open ( vlc_object_t * );
54     static void Close( vlc_object_t * );
55
56 vlc_module_begin();
57     set_shortname( _("UPnP"));
58     set_description( _("Universal Plug'n'Play discovery") );
59     set_category( CAT_PLAYLIST );
60     set_subcategory( SUBCAT_PLAYLIST_SD );
61
62     set_capability( "services_discovery", 0 );
63     set_callbacks( Open, Close );
64
65 vlc_module_end();
66
67
68 /*****************************************************************************
69  * Local structures
70  *****************************************************************************/
71
72 struct services_discovery_sys_t
73 {
74     /* playlist node */
75     playlist_item_t *p_node;
76     playlist_t *p_playlist;
77 };
78
79 /*****************************************************************************
80  * Local prototypes
81  *****************************************************************************/
82
83 /* Main functions */
84     static void Run    ( services_discovery_t *p_sd );
85
86 /*****************************************************************************
87  * Open: initialize and create stuff
88  *****************************************************************************/
89 static int Open( vlc_object_t *p_this )
90 {
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 ) );
94
95     playlist_view_t     *p_view;
96     vlc_value_t         val;
97
98     p_sd->pf_run = Run;
99     p_sd->p_sys  = p_sys;
100
101     /* Create our playlist node */
102     p_sys->p_playlist = (playlist_t *)vlc_object_find( p_sd,
103                                                        VLC_OBJECT_PLAYLIST,
104                                                        FIND_ANYWHERE );
105     if( !p_sys->p_playlist )
106     {
107         msg_Warn( p_sd, "unable to find playlist, cancelling UPnP listening");
108         return VLC_EGENERIC;
109     }
110
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 );
118
119     return VLC_SUCCESS;
120 }
121
122
123 /*****************************************************************************
124  * Close:
125  *****************************************************************************/
126 static void Close( vlc_object_t *p_this )
127 {
128     services_discovery_t *p_sd = ( services_discovery_t* )p_this;
129     services_discovery_sys_t    *p_sys  = p_sd->p_sys;
130
131     if( p_sys->p_playlist )
132     {
133         playlist_NodeDelete( p_sys->p_playlist, p_sys->p_node, VLC_TRUE,
134                              VLC_TRUE );
135         vlc_object_release( p_sys->p_playlist );
136     }
137
138     free( p_sys );
139 }
140
141 /*****************************************************************************
142  * Run: main UPnP thread
143  *****************************************************************************
144  * Processes UPnP events
145  *****************************************************************************/
146 class UPnPHandler : public MediaPlayer, public DeviceChangeListener,
147                     /*public EventListener,*/ public SearchResponseListener
148 {
149     private:
150         services_discovery_t *p_sd;
151         services_discovery_sys_t *p_sys;
152
153         Device *GetDeviceFromUSN( const string& usn )
154         {
155             return getDevice( usn.substr( 0, usn.find( "::" ) ).c_str() );
156         }
157
158         playlist_item_t *FindDeviceNode( Device *dev )
159         {
160             return playlist_ChildSearchName( p_sys->p_node, dev->getFriendlyName() );
161         }
162
163         playlist_item_t *FindDeviceNode( const string &usn )
164         {
165             return FindDeviceNode( GetDeviceFromUSN( usn ) );
166         }
167
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 );
172
173         /* CyberLink callbacks */
174         virtual void deviceAdded( Device *dev );
175         virtual void deviceRemoved( Device *dev );
176
177         virtual void deviceSearchResponseReceived( SSDPPacket *packet );
178         /*virtual void eventNotifyReceived( const char *uuid, long seq,
179                                           const char *name,
180                                           const char *value );*/
181
182     public:
183         UPnPHandler( services_discovery_t *p_this )
184             : p_sd( p_this ), p_sys( p_this->p_sys )
185         {
186             addDeviceChangeListener( this );
187             addSearchResponseListener( this );
188             //addEventListener( this );
189         }
190
191 };
192
193 static void Run( services_discovery_t *p_sd )
194 {
195     UPnPHandler u( p_sd );
196
197     u.start();
198
199     msg_Dbg( p_sd, "UPnP discovery started" );
200     /* read SAP packets */
201     while( !p_sd->b_die )
202     {
203         msleep( 500 );
204     }
205
206     u.stop();
207     msg_Dbg( p_sd, "UPnP discovery stopped" );
208 }
209
210
211 playlist_item_t *UPnPHandler::AddDevice( Device *dev )
212 {
213     if( dev == NULL )
214         return NULL;
215
216     /* We are not interested in IGD devices or whatever (at the moment) */
217     if ( !dev->isDeviceType( MediaServer::DEVICE_TYPE ) )
218         return NULL;
219
220     playlist_item_t *p_item = FindDeviceNode( dev );
221     if ( p_item != NULL )
222         return p_item;
223
224     /* FIXME:
225      * Maybe one day, VLC API will make sensible use of the const keyword;
226      * That day, you will no longer need this strdup().
227      */
228     char *str = strdup( dev->getFriendlyName( ) );
229
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 );
234     free( str );
235
236     return p_item;
237 }
238
239 void UPnPHandler::AddDeviceContent( Device *dev )
240 {
241     playlist_item_t *p_devnode = AddDevice( dev );
242
243     if( p_devnode == NULL )
244         return;
245
246     AddContent( p_devnode, getContentDirectory( dev ) );
247 }
248
249 void UPnPHandler::AddContent( playlist_item_t *p_parent, ContentNode *node )
250 {
251     if( node == NULL )
252         return;
253
254     const char *title = node->getTitle();
255     if( title == NULL )
256         return;
257
258     msg_Dbg( p_sd, "title = %s", title );
259     
260     if( !node->isContainerNode() )
261     {
262         msg_Dbg( p_sd, "not a container" );
263         return;
264     }
265
266     ContainerNode *conNode = (ContainerNode *)node;
267     ItemNode *iNode = (ItemNode *)node;
268
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 );
273
274     /*if( !cnode->hasContainerNodes() )
275         return;*/
276
277     unsigned nContentNodes = conNode->getNContentNodes();
278
279     for( unsigned n = 0; n < nContentNodes; n++ )
280         AddContent( p_item, conNode->getContentNode( n ) );
281 }
282
283
284 void UPnPHandler::RemoveDevice( Device *dev )
285 {
286     playlist_item_t *p_item = FindDeviceNode( dev );
287
288     if( p_item != NULL )
289         playlist_NodeDelete( p_sys->p_playlist, p_item, VLC_TRUE, VLC_TRUE );
290 }
291
292
293 void UPnPHandler::deviceAdded( Device *dev )
294 {
295     msg_Dbg( p_sd, "adding device" );
296     AddDeviceContent( dev );
297 }
298
299
300 void UPnPHandler::deviceRemoved( Device *dev )
301 {
302     msg_Dbg( p_sd, "removing device" );
303     RemoveDevice( dev );
304 }
305
306
307 void UPnPHandler::deviceSearchResponseReceived( SSDPPacket *packet )
308 {
309     if( !packet->isRootDevice() )
310         return;
311
312     string usn, nts, nt, udn;
313
314     packet->getUSN( usn );
315     packet->getNT( nt );
316     packet->getNTS( nts );
317     udn = usn.substr( 0, usn.find( "::" ) );
318
319     Device *dev = GetDeviceFromUSN( usn );
320     if( packet->isByeBye() )
321         RemoveDevice( dev );
322     else
323         AddDeviceContent( dev );
324 }
325
326 /*void UPnPHandler::eventNotifyReceived( const char *uuid, long seq,
327                                        const char *name, const char *value )
328 {
329     msg_Dbg( p_sd, "event notify received" );
330     msg_Dbg( p_sd, "uuid = %s, name = %s, value = %s", uuid, name, value );
331 }*/