Moves volume icon update in the timer
[vlc.git] / 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
32 #include <cybergarage/upnp/media/player/MediaPlayer.h>
33
34 #undef PACKAGE_NAME
35 #include <vlc/vlc.h>
36 #include <vlc_playlist.h>
37
38 /* FIXME: thread-safety ?? */
39 /* FIXME: playlist locking */
40
41 /************************************************************************
42  * Macros and definitions
43  ************************************************************************/
44 using namespace std;
45 using namespace CyberLink;
46
47 /*****************************************************************************
48  * Module descriptor
49  *****************************************************************************/
50
51 /* Callbacks */
52     static int  Open ( vlc_object_t * );
53     static void Close( vlc_object_t * );
54
55 vlc_module_begin();
56     set_shortname( "UPnP");
57     set_description( _("Universal Plug'n'Play discovery") );
58     set_category( CAT_PLAYLIST );
59     set_subcategory( SUBCAT_PLAYLIST_SD );
60
61     set_capability( "services_discovery", 0 );
62     set_callbacks( Open, Close );
63
64 vlc_module_end();
65
66
67 /*****************************************************************************
68  * Local structures
69  *****************************************************************************/
70
71 struct services_discovery_sys_t
72 {
73     playlist_item_t *p_node_one;
74     playlist_item_t *p_node_cat;
75 };
76
77 /*****************************************************************************
78  * Local prototypes
79  *****************************************************************************/
80
81 /* Main functions */
82     static void Run    ( services_discovery_t *p_sd );
83
84 /*****************************************************************************
85  * Open: initialize and create stuff
86  *****************************************************************************/
87 static int Open( vlc_object_t *p_this )
88 {
89     services_discovery_t *p_sd = ( services_discovery_t* )p_this;
90     services_discovery_sys_t *p_sys  = (services_discovery_sys_t *)
91                                 malloc( sizeof( services_discovery_sys_t ) );
92
93     p_sd->pf_run = Run;
94     p_sd->p_sys  = p_sys;
95
96     playlist_NodesPairCreate( pl_Get( p_sd ), _("Devices"),
97                               &p_sys->p_node_cat, &p_sys->p_node_one,
98                               VLC_TRUE );
99     return VLC_SUCCESS;
100 }
101
102
103 /*****************************************************************************
104  * Close:
105  *****************************************************************************/
106 static void Close( vlc_object_t *p_this )
107 {
108     services_discovery_t *p_sd = ( services_discovery_t* )p_this;
109     services_discovery_sys_t    *p_sys  = p_sd->p_sys;
110     playlist_t *p_playlist = pl_Yield( p_sd );
111     playlist_NodeDelete( p_playlist, p_sys->p_node_one, VLC_TRUE,
112                          VLC_TRUE );
113     playlist_NodeDelete( p_playlist, p_sys->p_node_cat, VLC_TRUE,
114                          VLC_TRUE );
115     pl_Release();
116     free( p_sys );
117 }
118
119 /*****************************************************************************
120  * Run: main UPnP thread
121  *****************************************************************************
122  * Processes UPnP events
123  *****************************************************************************/
124 class UPnPHandler : public MediaPlayer, public DeviceChangeListener,
125                     /*public EventListener,*/ public SearchResponseListener
126 {
127     private:
128         services_discovery_t *p_sd;
129         services_discovery_sys_t *p_sys;
130
131         Device *GetDeviceFromUSN( const string& usn )
132         {
133             return getDevice( usn.substr( 0, usn.find( "::" ) ).c_str() );
134         }
135
136         playlist_item_t *FindDeviceNode( Device *dev )
137         {
138             return playlist_ChildSearchName( p_sys->p_node, dev->getFriendlyName() );
139         }
140
141         playlist_item_t *FindDeviceNode( const string &usn )
142         {
143             return FindDeviceNode( GetDeviceFromUSN( usn ) );
144         }
145
146         playlist_item_t *AddDevice( Device *dev );
147         void AddDeviceContent( Device *dev );
148         void AddContent( playlist_item_t *p_parent, ContentNode *node );
149         void RemoveDevice( Device *dev );
150
151         /* CyberLink callbacks */
152         virtual void deviceAdded( Device *dev );
153         virtual void deviceRemoved( Device *dev );
154
155         virtual void deviceSearchResponseReceived( SSDPPacket *packet );
156         /*virtual void eventNotifyReceived( const char *uuid, long seq,
157                                           const char *name,
158                                           const char *value );*/
159
160     public:
161         UPnPHandler( services_discovery_t *p_this )
162             : p_sd( p_this ), p_sys( p_this->p_sys )
163         {
164             addDeviceChangeListener( this );
165             addSearchResponseListener( this );
166             //addEventListener( this );
167         }
168
169 };
170
171 static void Run( services_discovery_t *p_sd )
172 {
173     UPnPHandler u( p_sd );
174
175     u.start();
176
177     msg_Dbg( p_sd, "UPnP discovery started" );
178     /* read SAP packets */
179     while( !p_sd->b_die )
180     {
181         msleep( 500 );
182     }
183
184     u.stop();
185     msg_Dbg( p_sd, "UPnP discovery stopped" );
186 }
187
188
189 playlist_item_t *UPnPHandler::AddDevice( Device *dev )
190 {
191     if( dev == NULL )
192         return NULL;
193
194     /* We are not interested in IGD devices or whatever (at the moment) */
195     if ( !dev->isDeviceType( MediaServer::DEVICE_TYPE ) )
196         return NULL;
197
198     playlist_item_t *p_item = FindDeviceNode( dev );
199     if ( p_item != NULL )
200         return p_item;
201
202     /* FIXME:
203      * Maybe one day, VLC API will make sensible use of the const keyword;
204      * That day, you will no longer need this strdup().
205      */
206     char *str = strdup( dev->getFriendlyName( ) );
207
208     p_item = playlist_NodeCreate( p_sys->p_playlist, str, p_sys->p_node_cat,0 );
209     p_item->i_flags &= ~PLAYLIST_SKIP_FLAG;
210     msg_Dbg( p_sd, "device %s added", str );
211     free( str );
212
213     return p_item;
214 }
215
216 void UPnPHandler::AddDeviceContent( Device *dev )
217 {
218     playlist_item_t *p_devnode = AddDevice( dev );
219
220     if( p_devnode == NULL )
221         return;
222
223     AddContent( p_devnode, getContentDirectory( dev ) );
224 }
225
226 void UPnPHandler::AddContent( playlist_item_t *p_parent, ContentNode *node )
227 {
228     if( node == NULL )
229         return;
230
231     const char *title = node->getTitle();
232     if( title == NULL )
233         return;
234
235     msg_Dbg( p_sd, "title = %s", title );
236
237     if ( node->isItemNode() )
238     {
239         ItemNode *iNode = (ItemNode *)node;
240         input_item_t *p_input = input_ItemNew( p_sd, iNode->getResource(), title );
241         playlist_BothAddInput( p_sys->p_playlist, p_input, p_parent,
242                                PLAYLIST_APPEND, PLAYLIST_END, NULL, NULL,
243                                VLC_FALSE );
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 }*/