sd: use owner structure instead of legacy event manager
[vlc.git] / lib / media_discoverer.c
1 /*****************************************************************************
2  * media_discoverer.c: libvlc new API media discoverer functions
3  *****************************************************************************
4  * Copyright (C) 2007 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Pierre d'Herbemont <pdherbemont # videolan.org>
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 #include <assert.h>
29
30 #include <vlc/libvlc.h>
31 #include <vlc/libvlc_media.h>
32 #include <vlc/libvlc_media_list.h>
33 #include <vlc/libvlc_media_discoverer.h>
34 #include <vlc/libvlc_events.h>
35
36 #include <vlc_services_discovery.h>
37
38 #include "libvlc_internal.h"
39 #include "media_internal.h" // libvlc_media_new_from_input_item()
40 #include "media_list_internal.h" // libvlc_media_list_internal_add_media()
41
42 struct libvlc_media_discoverer_t
43 {
44     libvlc_event_manager_t * p_event_manager;
45     libvlc_instance_t *      p_libvlc_instance;
46     services_discovery_t *   p_sd;
47     libvlc_media_list_t *    p_mlist;
48     bool                     running;
49     vlc_dictionary_t         catname_to_submedialist;
50 };
51
52 /*
53  * Private functions
54  */
55
56 /**************************************************************************
57  *       services_discovery_item_added (Private) (VLC event callback)
58  **************************************************************************/
59
60 static void services_discovery_item_added( services_discovery_t *sd,
61                                            input_item_t *p_item,
62                                            const char *psz_cat )
63 {
64     libvlc_media_t * p_md;
65     libvlc_media_discoverer_t *p_mdis = sd->owner.sys;
66     libvlc_media_list_t * p_mlist = p_mdis->p_mlist;
67
68     p_md = libvlc_media_new_from_input_item( p_mdis->p_libvlc_instance,
69                                              p_item );
70
71     /* If we have a category, that mean we have to group the items having
72      * that category in a media_list. */
73     if( psz_cat )
74     {
75         p_mlist = vlc_dictionary_value_for_key( &p_mdis->catname_to_submedialist, psz_cat );
76
77         if( p_mlist == kVLCDictionaryNotFound )
78         {
79             libvlc_media_t * p_catmd;
80             p_catmd = libvlc_media_new_as_node( p_mdis->p_libvlc_instance, psz_cat );
81             p_mlist = libvlc_media_subitems( p_catmd );
82             p_mlist->b_read_only = true;
83
84             /* Insert the newly created mlist in our dictionary */
85             vlc_dictionary_insert( &p_mdis->catname_to_submedialist, psz_cat, p_mlist );
86
87             /* Insert the md into the root list */
88             libvlc_media_list_lock( p_mdis->p_mlist );
89             libvlc_media_list_internal_add_media( p_mdis->p_mlist, p_catmd );
90             libvlc_media_list_unlock( p_mdis->p_mlist );
91
92             /* We don't release the mlist cause the dictionary
93              * doesn't retain the object. But we release the md. */
94             libvlc_media_release( p_catmd );
95         }
96     }
97
98     libvlc_media_list_lock( p_mlist );
99     libvlc_media_list_internal_add_media( p_mlist, p_md );
100     libvlc_media_list_unlock( p_mlist );
101
102     libvlc_media_release( p_md );
103 }
104
105 /**************************************************************************
106  *       services_discovery_item_removed (Private) (VLC event callback)
107  **************************************************************************/
108
109 static void services_discovery_item_removed( services_discovery_t *sd,
110                                              input_item_t *p_item )
111 {
112     libvlc_media_t * p_md;
113     libvlc_media_discoverer_t *p_mdis = sd->owner.sys;
114
115     int i, count = libvlc_media_list_count( p_mdis->p_mlist );
116     libvlc_media_list_lock( p_mdis->p_mlist );
117     for( i = 0; i < count; i++ )
118     {
119         p_md = libvlc_media_list_item_at_index( p_mdis->p_mlist, i );
120         assert(p_md != NULL);
121         if( p_md->p_input_item == p_item )
122         {
123             libvlc_media_list_internal_remove_index( p_mdis->p_mlist, i );
124             libvlc_media_release( p_md );
125             break;
126         }
127         libvlc_media_release( p_md );
128     }
129     libvlc_media_list_unlock( p_mdis->p_mlist );
130 }
131
132 /*
133  * Public libvlc functions
134  */
135
136 /**************************************************************************
137  *       new (Public)
138  **************************************************************************/
139 libvlc_media_discoverer_t *
140 libvlc_media_discoverer_new( libvlc_instance_t * p_inst, const char * psz_name )
141 {
142     /* podcast SD is a hack and only works with custom playlist callbacks. */
143     if( !strncasecmp( psz_name, "podcast", 7 ) )
144         return NULL;
145
146     libvlc_media_discoverer_t *p_mdis = malloc(sizeof(*p_mdis));
147     if( unlikely(!p_mdis) )
148     {
149         libvlc_printerr( "Not enough memory" );
150         return NULL;
151     }
152
153     p_mdis->p_libvlc_instance = p_inst;
154     p_mdis->p_mlist = libvlc_media_list_new( p_inst );
155     p_mdis->p_mlist->b_read_only = true;
156     p_mdis->running = false;
157
158     vlc_dictionary_init( &p_mdis->catname_to_submedialist, 0 );
159
160     p_mdis->p_event_manager = libvlc_event_manager_new( p_mdis );
161     if( unlikely(p_mdis->p_event_manager == NULL) )
162     {
163         free( p_mdis );
164         return NULL;
165     }
166
167     struct services_discovery_owner_t owner = {
168         p_mdis,
169         services_discovery_item_added,
170         services_discovery_item_removed,
171     };
172
173     p_mdis->p_sd = vlc_sd_Create( (vlc_object_t*)p_inst->p_libvlc_int,
174                                   psz_name, &owner );
175     if( unlikely(p_mdis->p_sd == NULL) )
176     {
177         libvlc_printerr( "%s: no such discovery module found", psz_name );
178         libvlc_media_list_release( p_mdis->p_mlist );
179         libvlc_event_manager_release( p_mdis->p_event_manager );
180         free( p_mdis );
181         return NULL;
182     }
183
184     libvlc_retain( p_inst );
185     return p_mdis;
186 }
187
188 /**************************************************************************
189  *       start (Public)
190  **************************************************************************/
191 LIBVLC_API int
192 libvlc_media_discoverer_start( libvlc_media_discoverer_t * p_mdis )
193 {
194     /* Here we go */
195     if (!vlc_sd_Start( p_mdis->p_sd ))
196         return -1;
197
198     p_mdis->running = true;
199     libvlc_event_t event;
200     event.type = libvlc_MediaDiscovererStarted;
201     libvlc_event_send( p_mdis->p_event_manager, &event );
202     return 0;
203 }
204
205 /**************************************************************************
206  *       stop (Public)
207  **************************************************************************/
208 LIBVLC_API void
209 libvlc_media_discoverer_stop( libvlc_media_discoverer_t * p_mdis )
210 {
211     p_mdis->running = false;
212
213     libvlc_media_list_t * p_mlist = p_mdis->p_mlist;
214     libvlc_media_list_lock( p_mlist );
215     libvlc_media_list_internal_end_reached( p_mlist );
216     libvlc_media_list_unlock( p_mlist );
217
218     libvlc_event_t event;
219     event.type = libvlc_MediaDiscovererEnded;
220     libvlc_event_send( p_mdis->p_event_manager, &event );
221
222     vlc_sd_Stop( p_mdis->p_sd );
223 }
224
225 /**************************************************************************
226  *       new_from_name (Public)
227  *
228  * \deprecated Use libvlc_media_discoverer_new and libvlc_media_discoverer_start
229  **************************************************************************/
230 libvlc_media_discoverer_t *
231 libvlc_media_discoverer_new_from_name( libvlc_instance_t * p_inst,
232                                        const char * psz_name )
233 {
234     libvlc_media_discoverer_t *p_mdis = libvlc_media_discoverer_new( p_inst, psz_name );
235
236     if( !p_mdis )
237         return NULL;
238
239     if( libvlc_media_discoverer_start( p_mdis ) != 0)
240     {
241         libvlc_media_discoverer_release( p_mdis );
242         return NULL;
243     }
244
245     return p_mdis;
246 }
247
248 /**************************************************************************
249  * release (Public)
250  **************************************************************************/
251 void
252 libvlc_media_discoverer_release( libvlc_media_discoverer_t * p_mdis )
253 {
254     if( p_mdis->running )
255         libvlc_media_discoverer_stop( p_mdis );
256
257     vlc_sd_Destroy( p_mdis->p_sd );
258
259     libvlc_media_list_release( p_mdis->p_mlist );
260
261     /* Free catname_to_submedialist and all the mlist */
262     char ** all_keys = vlc_dictionary_all_keys( &p_mdis->catname_to_submedialist );
263     for( int i = 0; all_keys[i]; i++ )
264     {
265         libvlc_media_list_t * p_catmlist = vlc_dictionary_value_for_key( &p_mdis->catname_to_submedialist, all_keys[i] );
266         libvlc_media_list_release( p_catmlist );
267         free( all_keys[i] );
268     }
269     free( all_keys );
270
271     vlc_dictionary_clear( &p_mdis->catname_to_submedialist, NULL, NULL );
272     libvlc_event_manager_release( p_mdis->p_event_manager );
273     libvlc_release( p_mdis->p_libvlc_instance );
274
275     free( p_mdis );
276 }
277
278 /**************************************************************************
279  * localized_name (Public)
280  **************************************************************************/
281 char *
282 libvlc_media_discoverer_localized_name( libvlc_media_discoverer_t * p_mdis )
283 {
284     return services_discovery_GetLocalizedName( p_mdis->p_sd );
285 }
286
287 /**************************************************************************
288  * media_list (Public)
289  **************************************************************************/
290 libvlc_media_list_t *
291 libvlc_media_discoverer_media_list( libvlc_media_discoverer_t * p_mdis )
292 {
293     libvlc_media_list_retain( p_mdis->p_mlist );
294     return p_mdis->p_mlist;
295 }
296
297 /**************************************************************************
298  * event_manager (Public)
299  **************************************************************************/
300 libvlc_event_manager_t *
301 libvlc_media_discoverer_event_manager( libvlc_media_discoverer_t * p_mdis )
302 {
303     return p_mdis->p_event_manager;
304 }
305
306
307 /**************************************************************************
308  * running (Public)
309  **************************************************************************/
310 int
311 libvlc_media_discoverer_is_running( libvlc_media_discoverer_t * p_mdis )
312 {
313     return p_mdis->running;
314 }
315
316 void
317 libvlc_media_discoverer_list_release( libvlc_media_discoverer_description_t **pp_services,
318                                       size_t i_count )
319 {
320     if( i_count > 0 )
321     {
322         for( size_t i = 0; i < i_count; ++i )
323         {
324             free( pp_services[i]->psz_name );
325             free( pp_services[i]->psz_longname );
326         }
327         free( *pp_services );
328         free( pp_services );
329     }
330 }
331
332 size_t
333 libvlc_media_discoverer_list_get( libvlc_instance_t *p_inst,
334                                   libvlc_media_discoverer_category_t i_cat,
335                                   libvlc_media_discoverer_description_t ***ppp_services )
336 {
337     assert( p_inst != NULL && ppp_services != NULL );
338
339     int i_core_cat;
340     switch( i_cat )
341     {
342     case libvlc_media_discoverer_devices:
343         i_core_cat = SD_CAT_DEVICES;
344         break;
345     case libvlc_media_discoverer_lan:
346         i_core_cat = SD_CAT_LAN;
347         break;
348     case libvlc_media_discoverer_podcasts:
349         i_core_cat = SD_CAT_INTERNET;
350         break;
351     case libvlc_media_discoverer_localdirs:
352         i_core_cat = SD_CAT_MYCOMPUTER;
353         break;
354     default:
355         vlc_assert_unreachable();
356         *ppp_services = NULL;
357         return 0;
358     }
359
360     /* Fetch all sd names, longnames and categories */
361     char **ppsz_names, **ppsz_longnames;
362     int *p_categories;
363     ppsz_names = vlc_sd_GetNames( p_inst->p_libvlc_int, &ppsz_longnames,
364                                   &p_categories );
365
366     if( ppsz_names == NULL )
367     {
368         *ppp_services = NULL;
369         return 0;
370     }
371
372     /* Count the number of sd matching our category (i_cat/i_core_cat) */
373     size_t i_nb_services = 0;
374     char **ppsz_name = ppsz_names;
375     int *p_category = p_categories;
376     for( ; *ppsz_name != NULL; ppsz_name++, p_category++ )
377     {
378         if( *p_category == i_core_cat )
379             i_nb_services++;
380     }
381
382     libvlc_media_discoverer_description_t **pp_services = NULL, *p_services = NULL;
383     if( i_nb_services > 0 )
384     {
385         /* Double alloc here, so that the caller iterates through pointers of
386          * struct instead of structs. This allows us to modify the struct
387          * without breaking the API. */
388
389         pp_services = malloc( i_nb_services
390                               * sizeof(libvlc_media_discoverer_description_t *) );
391         p_services = malloc( i_nb_services
392                              * sizeof(libvlc_media_discoverer_description_t) );
393         if( pp_services == NULL || p_services == NULL )
394         {
395             free( pp_services );
396             free( p_services );
397             pp_services = NULL;
398             p_services = NULL;
399             i_nb_services = 0;
400             /* Even if alloc fails, the next loop must be run in order to free
401              * names returned by vlc_sd_GetNames */
402         }
403     }
404
405     /* Fill output pp_services or free unused name, longname */
406     char **ppsz_longname = ppsz_longnames;
407     ppsz_name = ppsz_names;
408     p_category = p_categories;
409     unsigned int i_service_idx = 0;
410     libvlc_media_discoverer_description_t *p_service = p_services;
411     for( ; *ppsz_name != NULL; ppsz_name++, ppsz_longname++, p_category++ )
412     {
413         if( pp_services != NULL && *p_category == i_core_cat )
414         {
415             p_service->psz_name = *ppsz_name;
416             p_service->psz_longname = *ppsz_longname;
417             p_service->i_cat = i_cat;
418             pp_services[i_service_idx++] = p_service++;
419         }
420         else
421         {
422             free( *ppsz_name );
423             free( *ppsz_longname );
424         }
425     }
426     free( ppsz_names );
427     free( ppsz_longnames );
428     free( p_categories );
429
430     *ppp_services = pp_services;
431     return i_nb_services;
432 }