]> git.sesse.net Git - vlc/blob - modules/services_discovery/upnp_cc.cpp
* Do not take and release the structure lock for each element of the list while
[vlc] / modules / services_discovery / upnp_cc.cpp
1 /*****************************************************************************
2  * upnp_cc.cpp :  UPnP discovery module
3  *****************************************************************************
4  * Copyright (C) 2004-2005 the VideoLAN team
5  * $Id$
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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, 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_playlist.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_item_t *p_node_one;
75     playlist_item_t *p_node_cat;
76 };
77
78 /*****************************************************************************
79  * Local prototypes
80  *****************************************************************************/
81
82 /* Main functions */
83     static void Run    ( services_discovery_t *p_sd );
84
85 /*****************************************************************************
86  * Open: initialize and create stuff
87  *****************************************************************************/
88 static int Open( vlc_object_t *p_this )
89 {
90     services_discovery_t *p_sd = ( services_discovery_t* )p_this;
91     services_discovery_sys_t *p_sys  = (services_discovery_sys_t *)
92                                 malloc( sizeof( services_discovery_sys_t ) );
93
94     p_sd->pf_run = Run;
95     p_sd->p_sys  = p_sys;
96
97     playlist_NodesPairCreate( pl_Get( p_sd ), _("Devices"),
98                               &p_sys->p_node_cat, &p_sys->p_node_one,
99                               VLC_TRUE );
100     return VLC_SUCCESS;
101 }
102
103
104 /*****************************************************************************
105  * Close:
106  *****************************************************************************/
107 static void Close( vlc_object_t *p_this )
108 {
109     services_discovery_t *p_sd = ( services_discovery_t* )p_this;
110     services_discovery_sys_t    *p_sys  = p_sd->p_sys;
111     playlist_t *p_playlist = pl_Yield( p_sd );
112     playlist_NodeDelete( p_playlist, p_sys->p_node_one, VLC_TRUE,
113                          VLC_TRUE );
114     playlist_NodeDelete( p_playlist, p_sys->p_node_cat, VLC_TRUE,
115                          VLC_TRUE );
116     pl_Release();
117     free( p_sys );
118 }
119
120 /*****************************************************************************
121  * Run: main UPnP thread
122  *****************************************************************************
123  * Processes UPnP events
124  *****************************************************************************/
125 class UPnPHandler : public MediaPlayer, public DeviceChangeListener,
126                     /*public EventListener,*/ public SearchResponseListener
127 {
128     private:
129         services_discovery_t *p_sd;
130         services_discovery_sys_t *p_sys;
131
132         Device *GetDeviceFromUSN( const string& usn )
133         {
134             return getDevice( usn.substr( 0, usn.find( "::" ) ).c_str() );
135         }
136
137         playlist_item_t *FindDeviceNode( Device *dev )
138         {
139             return playlist_ChildSearchName( p_sys->p_node, dev->getFriendlyName() );
140         }
141
142         playlist_item_t *FindDeviceNode( const string &usn )
143         {
144             return FindDeviceNode( GetDeviceFromUSN( usn ) );
145         }
146
147         playlist_item_t *AddDevice( Device *dev );
148         void AddDeviceContent( Device *dev );
149         void AddContent( playlist_item_t *p_parent, ContentNode *node );
150         void RemoveDevice( Device *dev );
151
152         /* CyberLink callbacks */
153         virtual void deviceAdded( Device *dev );
154         virtual void deviceRemoved( Device *dev );
155
156         virtual void deviceSearchResponseReceived( SSDPPacket *packet );
157         /*virtual void eventNotifyReceived( const char *uuid, long seq,
158                                           const char *name,
159                                           const char *value );*/
160
161     public:
162         UPnPHandler( services_discovery_t *p_this )
163             : p_sd( p_this ), p_sys( p_this->p_sys )
164         {
165             addDeviceChangeListener( this );
166             addSearchResponseListener( this );
167             //addEventListener( this );
168         }
169
170 };
171
172 static void Run( services_discovery_t *p_sd )
173 {
174     UPnPHandler u( p_sd );
175
176     u.start();
177
178     msg_Dbg( p_sd, "UPnP discovery started" );
179     /* read SAP packets */
180     while( !p_sd->b_die )
181     {
182         msleep( 500 );
183     }
184
185     u.stop();
186     msg_Dbg( p_sd, "UPnP discovery stopped" );
187 }
188
189
190 playlist_item_t *UPnPHandler::AddDevice( Device *dev )
191 {
192     if( dev == NULL )
193         return NULL;
194
195     /* We are not interested in IGD devices or whatever (at the moment) */
196     if ( !dev->isDeviceType( MediaServer::DEVICE_TYPE ) )
197         return NULL;
198
199     playlist_item_t *p_item = FindDeviceNode( dev );
200     if ( p_item != NULL )
201         return p_item;
202
203     /* FIXME:
204      * Maybe one day, VLC API will make sensible use of the const keyword;
205      * That day, you will no longer need this strdup().
206      */
207     char *str = strdup( dev->getFriendlyName( ) );
208
209     p_item = playlist_NodeCreate( p_sys->p_playlist, str, p_sys->p_node_cat,0 );
210     p_item->i_flags &= ~PLAYLIST_SKIP_FLAG;
211     msg_Dbg( p_sd, "device %s added", str );
212     free( str );
213
214     return p_item;
215 }
216
217 void UPnPHandler::AddDeviceContent( Device *dev )
218 {
219     playlist_item_t *p_devnode = AddDevice( dev );
220
221     if( p_devnode == NULL )
222         return;
223
224     AddContent( p_devnode, getContentDirectory( dev ) );
225 }
226
227 void UPnPHandler::AddContent( playlist_item_t *p_parent, ContentNode *node )
228 {
229     if( node == NULL )
230         return;
231
232     const char *title = node->getTitle();
233     if( title == NULL )
234         return;
235
236     msg_Dbg( p_sd, "title = %s", title );
237
238     if ( node->isItemNode() )
239     {
240         ItemNode *iNode = (ItemNode *)node;
241         input_item_t *p_input = input_ItemNew( p_sd, iNode->getResource(), title );
242         playlist_BothAddInput( p_sys->p_playlist, p_input, p_parent,
243                                PLAYLIST_APPEND, PLAYLIST_END, NULL, NULL );
244     } else if ( node->isContainerNode() )
245     {
246         ContainerNode *conNode = (ContainerNode *)node;
247
248         char* p_name = strdup(title); /* See other comment on strdup */
249         playlist_item_t* p_node = playlist_NodeCreate( p_sys->p_playlist,
250                                                        p_name, p_parent, 0 );
251         free(p_name);
252
253         unsigned nContentNodes = conNode->getNContentNodes();
254
255         for( unsigned n = 0; n < nContentNodes; n++ )
256            AddContent( p_node, conNode->getContentNode( n ) );
257     }
258 }
259
260
261 void UPnPHandler::RemoveDevice( Device *dev )
262 {
263     playlist_item_t *p_item = FindDeviceNode( dev );
264
265     if( p_item != NULL )
266         playlist_NodeDelete( p_sys->p_playlist, p_item, VLC_TRUE, VLC_TRUE );
267 }
268
269
270 void UPnPHandler::deviceAdded( Device *dev )
271 {
272     msg_Dbg( p_sd, "adding device" );
273     AddDeviceContent( dev );
274 }
275
276
277 void UPnPHandler::deviceRemoved( Device *dev )
278 {
279     msg_Dbg( p_sd, "removing device" );
280     RemoveDevice( dev );
281 }
282
283
284 void UPnPHandler::deviceSearchResponseReceived( SSDPPacket *packet )
285 {
286     if( !packet->isRootDevice() )
287         return;
288
289     string usn, nts, nt, udn;
290
291     packet->getUSN( usn );
292     packet->getNT( nt );
293     packet->getNTS( nts );
294     udn = usn.substr( 0, usn.find( "::" ) );
295
296     /* Remove existing root device before adding updated one */
297
298     Device *dev = GetDeviceFromUSN( usn );
299     RemoveDevice( dev );
300
301     if( !packet->isByeBye() )
302         AddDeviceContent( dev );
303 }
304
305 /*void UPnPHandler::eventNotifyReceived( const char *uuid, long seq,
306                                        const char *name, const char *value )
307 {
308     msg_Dbg( p_sd, "event notify received" );
309     msg_Dbg( p_sd, "uuid = %s, name = %s, value = %s", uuid, name, value );
310 }*/