]> git.sesse.net Git - vlc/blob - src/playlist/services_discovery.c
sd: Factorize function dispatch for better code readability.
[vlc] / src / playlist / services_discovery.c
1 /*****************************************************************************
2  * services_discovery.c : Manage playlist services_discovery modules
3  *****************************************************************************
4  * Copyright (C) 1999-2004 the VideoLAN team
5  * $Id$
6  *
7  * Authors: ClĂ©ment Stenac <zorglub@videolan.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23 #ifdef HAVE_CONFIG_H
24 # include "config.h"
25 #endif
26 #include <assert.h>
27
28 #include <vlc_common.h>
29 #include "vlc_playlist.h"
30 #include "vlc_events.h"
31 #include <vlc_services_discovery.h>
32 #include <vlc_probe.h>
33 #include "playlist_internal.h"
34 #include "../libvlc.h"
35
36 typedef struct
37 {
38     char *name;
39     char *longname;
40     int category;
41 } vlc_sd_probe_t;
42
43 int vlc_sd_probe_Add (vlc_probe_t *probe, const char *name,
44                       const char *longname, int category)
45 {
46     vlc_sd_probe_t names = { strdup(name), strdup(longname), category };
47
48     if (unlikely (names.name == NULL || names.longname == NULL
49                || vlc_probe_add (probe, &names, sizeof (names))))
50     {
51         free (names.name);
52         free (names.longname);
53         return VLC_ENOMEM;
54     }
55     return VLC_PROBE_CONTINUE;
56 }
57
58 #undef vlc_sd_GetNames
59
60 /**
61  * Gets the list of available services discovery plugins.
62  */
63 char **vlc_sd_GetNames (vlc_object_t *obj, char ***pppsz_longnames, int **pp_categories)
64 {
65     size_t count;
66     vlc_sd_probe_t *tab = vlc_probe (obj, "services probe", &count);
67
68     if (count == 0)
69     {
70         free (tab);
71         return NULL;
72     }
73
74     char **names = malloc (sizeof(char *) * (count + 1));
75     char **longnames = malloc (sizeof(char *) * (count + 1));
76     int *categories = malloc(sizeof(int) * (count + 1));
77
78     if (unlikely (names == NULL || longnames == NULL || categories == NULL))
79         abort();
80     for( size_t i = 0; i < count; i++ )
81     {
82         names[i] = tab[i].name;
83         longnames[i] = tab[i].longname;
84         categories[i] = tab[i].category;
85     }
86     free (tab);
87     names[count] = longnames[count] = NULL;
88     categories[count] = 0;
89     *pppsz_longnames = longnames;
90     if( pp_categories ) *pp_categories = categories;
91     else free( categories );
92     return names;
93 }
94
95
96 static void services_discovery_Destructor ( vlc_object_t *p_obj );
97
98 /*
99  * Services discovery
100  * Basically you just listen to Service discovery event through the
101  * sd's event manager.
102  * That's how the playlist get's Service Discovery information
103  */
104
105 /***********************************************************************
106  * Create
107  ***********************************************************************/
108 services_discovery_t *vlc_sd_Create( vlc_object_t *p_super,
109                                      const char *cfg )
110 {
111     services_discovery_t *p_sd;
112
113     p_sd = vlc_custom_create( p_super, sizeof( *p_sd ), VLC_OBJECT_GENERIC,
114                               "services discovery" );
115     if( !p_sd )
116         return NULL;
117     free(config_ChainCreate( &p_sd->psz_name, &p_sd->p_cfg, cfg ));
118
119     vlc_event_manager_t *em = &p_sd->event_manager;
120     vlc_event_manager_init( em, p_sd, (vlc_object_t *)p_sd );
121     vlc_event_manager_register_event_type(em, vlc_ServicesDiscoveryItemAdded);
122     vlc_event_manager_register_event_type(em, vlc_ServicesDiscoveryItemRemoved);
123     vlc_event_manager_register_event_type(em, vlc_ServicesDiscoveryStarted);
124     vlc_event_manager_register_event_type(em, vlc_ServicesDiscoveryEnded);
125
126     vlc_object_set_destructor( p_sd, services_discovery_Destructor );
127     vlc_object_attach( p_sd, p_super );
128
129     return p_sd;
130 }
131
132 /***********************************************************************
133  * Stop
134  ***********************************************************************/
135 bool vlc_sd_Start ( services_discovery_t * p_sd )
136 {
137     assert(!p_sd->p_module);
138
139     p_sd->p_module = module_need( p_sd, "services_discovery",
140                                   p_sd->psz_name, true );
141     if( p_sd->p_module == NULL )
142     {
143         msg_Err( p_sd, "no suitable services discovery module" );
144         return false;
145     }
146
147     vlc_event_t event = {
148         .type = vlc_ServicesDiscoveryStarted
149     };
150     vlc_event_send( &p_sd->event_manager, &event );
151     return true;
152 }
153
154 /***********************************************************************
155  * Stop
156  ***********************************************************************/
157 void vlc_sd_Stop ( services_discovery_t * p_sd )
158 {
159     vlc_event_t event = {
160         .type = vlc_ServicesDiscoveryEnded
161     };
162
163     vlc_event_send( &p_sd->event_manager, &event );
164
165     module_unneed( p_sd, p_sd->p_module );
166     p_sd->p_module = NULL;
167 }
168
169 void vlc_sd_Destroy( services_discovery_t *p_sd )
170 {
171     config_ChainDestroy( p_sd->p_cfg );
172     free( p_sd->psz_name );
173     vlc_object_release( p_sd );
174 }
175
176 /***********************************************************************
177  * Destructor
178  ***********************************************************************/
179 static void services_discovery_Destructor ( vlc_object_t *p_obj )
180 {
181     services_discovery_t * p_sd = (services_discovery_t *)p_obj;
182     assert(!p_sd->p_module); /* Forgot to call Stop */
183     vlc_event_manager_fini( &p_sd->event_manager );
184 }
185
186 /***********************************************************************
187  * GetLocalizedName
188  ***********************************************************************/
189 char *
190 services_discovery_GetLocalizedName ( services_discovery_t * p_sd )
191 {
192     return strdup( module_get_name( p_sd->p_module, true ) );
193 }
194
195 /***********************************************************************
196  * EventManager
197  ***********************************************************************/
198 vlc_event_manager_t *
199 services_discovery_EventManager ( services_discovery_t * p_sd )
200 {
201     return &p_sd->event_manager;
202 }
203
204 /***********************************************************************
205  * AddItem
206  ***********************************************************************/
207 void
208 services_discovery_AddItem ( services_discovery_t * p_sd, input_item_t * p_item,
209                              const char * psz_category )
210 {
211     vlc_event_t event;
212     event.type = vlc_ServicesDiscoveryItemAdded;
213     event.u.services_discovery_item_added.p_new_item = p_item;
214     event.u.services_discovery_item_added.psz_category = psz_category;
215
216     vlc_event_send( &p_sd->event_manager, &event );
217 }
218
219 /***********************************************************************
220  * RemoveItem
221  ***********************************************************************/
222 void
223 services_discovery_RemoveItem ( services_discovery_t * p_sd, input_item_t * p_item )
224 {
225     vlc_event_t event;
226     event.type = vlc_ServicesDiscoveryItemRemoved;
227     event.u.services_discovery_item_removed.p_item = p_item;
228
229     vlc_event_send( &p_sd->event_manager, &event );
230 }
231
232 /*
233  * Playlist - Services discovery bridge
234  */
235
236 struct vlc_sd_internal_t
237 {
238     /* the playlist items for category and onelevel */
239     playlist_item_t      *p_node;
240     services_discovery_t *p_sd; /**< Loaded service discovery modules */
241     char                 *psz_name;
242 };
243
244  /* A new item has been added to a certain sd */
245 static void playlist_sd_item_added( const vlc_event_t * p_event, void * user_data )
246 {
247     input_item_t * p_input = p_event->u.services_discovery_item_added.p_new_item;
248     const char * psz_cat = p_event->u.services_discovery_item_added.psz_category;
249     playlist_item_t * p_parent = user_data;
250     playlist_t * p_playlist = p_parent->p_playlist;
251
252     msg_Dbg( p_playlist, "Adding %s in %s",
253                 p_input->psz_name ? p_input->psz_name : "(null)",
254                 psz_cat ? psz_cat : "(null)" );
255
256     PL_LOCK;
257     /* If p_parent is in root category (this is clearly a hack) and we have a cat */
258     if( !EMPTY_STR(psz_cat) )
259     {
260         /* */
261         playlist_item_t * p_cat;
262         p_cat = playlist_ChildSearchName( p_parent, psz_cat );
263         if( !p_cat )
264         {
265             p_cat = playlist_NodeCreate( p_playlist, psz_cat,
266                                          p_parent, 0, NULL );
267             p_cat->i_flags &= ~PLAYLIST_SKIP_FLAG;
268         }
269         p_parent = p_cat;
270     }
271
272     playlist_NodeAddInput( p_playlist, p_input, p_parent,
273                            PLAYLIST_APPEND, PLAYLIST_END,
274                            pl_Locked );
275     PL_UNLOCK;
276 }
277
278  /* A new item has been removed from a certain sd */
279 static void playlist_sd_item_removed( const vlc_event_t * p_event, void * user_data )
280 {
281     input_item_t * p_input = p_event->u.services_discovery_item_removed.p_item;
282     playlist_item_t * p_sd_node = user_data;
283     playlist_t *p_playlist = p_sd_node->p_playlist;
284
285     PL_LOCK;
286     playlist_item_t *p_item =
287         playlist_ItemFindFromInputAndRoot( p_playlist, p_input,
288                                            p_sd_node, false );
289     if( !p_item )
290     {
291         PL_UNLOCK; return;
292     }
293     playlist_item_t *p_parent = p_item->p_parent;
294     /* if the item was added under a category and the category node
295        becomes empty, delete that node as well */
296     if( p_parent->i_children > 1 || p_parent == p_sd_node )
297         playlist_DeleteItem( p_playlist, p_item, true );
298     else
299         playlist_NodeDelete( p_playlist, p_parent, true, true );
300     PL_UNLOCK;
301 }
302
303 int playlist_ServicesDiscoveryAdd( playlist_t *p_playlist,
304                                    const char *psz_name )
305 {
306     /* Perform the addition */
307     services_discovery_t *p_sd;
308
309     msg_Dbg( p_playlist, "adding services_discovery %s...", psz_name );
310     p_sd = vlc_sd_Create( VLC_OBJECT(p_playlist), psz_name );
311     if( !p_sd )
312         return VLC_ENOMEM;
313
314     /* Free in playlist_ServicesDiscoveryRemove */
315     vlc_sd_internal_t * p_sds = malloc( sizeof(*p_sds) );
316     if( !p_sds )
317     {
318         vlc_sd_Destroy( p_sd );
319         return VLC_ENOMEM;
320     }
321
322     playlist_item_t *p_node;
323
324     /* Look for configuration chain "longname" */
325     const char *psz_longname = "?";
326     if( p_sd->p_cfg )
327     {
328         config_chain_t *cfg = p_sd->p_cfg;
329         while( cfg )
330         {
331             if( cfg->psz_name && !strcmp( cfg->psz_name, "longname" ) )
332             {
333                 psz_longname = cfg->psz_value;
334                 break;
335             }
336             cfg = cfg->p_next;
337         }
338     }
339
340     PL_LOCK;
341     p_node = playlist_NodeCreate( p_playlist, psz_longname,
342                                   p_playlist->p_root, 0, NULL );
343     PL_UNLOCK;
344
345     vlc_event_manager_t *em = services_discovery_EventManager( p_sd );
346     vlc_event_attach( em, vlc_ServicesDiscoveryItemAdded,
347                       playlist_sd_item_added, p_node );
348
349     vlc_event_attach( em, vlc_ServicesDiscoveryItemRemoved,
350                       playlist_sd_item_removed, p_node );
351
352     if( !vlc_sd_Start( p_sd ) )
353     {
354         vlc_sd_Destroy( p_sd );
355         free( p_sds );
356         return VLC_EGENERIC;
357     }
358
359     /* We want tree-view for service directory */
360     p_node->p_input->b_prefers_tree = true;
361     p_sds->p_sd = p_sd;
362     p_sds->p_node = p_node;
363     p_sds->psz_name = strdup( psz_name );
364
365     PL_LOCK;
366     TAB_APPEND( pl_priv(p_playlist)->i_sds, pl_priv(p_playlist)->pp_sds, p_sds );
367     PL_UNLOCK;
368
369     return VLC_SUCCESS;
370 }
371
372 int playlist_ServicesDiscoveryRemove( playlist_t * p_playlist,
373                                       const char *psz_name )
374 {
375     playlist_private_t *priv = pl_priv( p_playlist );
376     vlc_sd_internal_t * p_sds = NULL;
377
378     PL_LOCK;
379     for( int i = 0; i < priv->i_sds; i++ )
380     {
381         if( !strcmp( psz_name, priv->pp_sds[i]->psz_name ) )
382         {
383             p_sds = priv->pp_sds[i];
384             REMOVE_ELEM( priv->pp_sds, priv->i_sds, i );
385             break;
386         }
387     }
388     PL_UNLOCK;
389
390     if( !p_sds )
391     {
392         msg_Warn( p_playlist, "discovery %s is not loaded", psz_name );
393         return VLC_EGENERIC;
394     }
395
396     services_discovery_t *p_sd = p_sds->p_sd;
397     assert( p_sd );
398
399     vlc_sd_Stop( p_sd );
400
401     vlc_event_detach( services_discovery_EventManager( p_sd ),
402                         vlc_ServicesDiscoveryItemAdded,
403                         playlist_sd_item_added,
404                         p_sds->p_node );
405
406     vlc_event_detach( services_discovery_EventManager( p_sd ),
407                         vlc_ServicesDiscoveryItemRemoved,
408                         playlist_sd_item_removed,
409                         p_sds->p_node );
410
411     /* Remove the sd playlist node if it exists */
412     PL_LOCK;
413     playlist_NodeDelete( p_playlist, p_sds->p_node, true, false );
414     PL_UNLOCK;
415
416     vlc_sd_Destroy( p_sd );
417     free( p_sds->psz_name );
418     free( p_sds );
419
420     return VLC_SUCCESS;
421 }
422
423 bool playlist_IsServicesDiscoveryLoaded( playlist_t * p_playlist,
424                                          const char *psz_name )
425 {
426     playlist_private_t *priv = pl_priv( p_playlist );
427     bool found = false;
428     PL_LOCK;
429
430     for( int i = 0; i < priv->i_sds; i++ )
431     {
432         vlc_sd_internal_t *sd = priv->pp_sds[i];
433
434         if( sd->psz_name && !strcmp( psz_name, sd->psz_name ) )
435         {
436             found = true;
437             break;
438         }
439     }
440     PL_UNLOCK;
441     return found;
442 }
443
444 void playlist_ServicesDiscoveryKillAll( playlist_t *p_playlist )
445 {
446     playlist_private_t *priv = pl_priv( p_playlist );
447
448     while( priv->i_sds > 0 )
449         playlist_ServicesDiscoveryRemove( p_playlist,
450                                           priv->pp_sds[0]->psz_name );
451 }