1 /*****************************************************************************
2 * services_discovery.c : Manage playlist services_discovery modules
3 *****************************************************************************
4 * Copyright (C) 1999-2004 VLC authors and VideoLAN
7 * Authors: Clément Stenac <zorglub@videolan.org>
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.
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.
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 *****************************************************************************/
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 <vlc_modules.h>
34 #include "playlist_internal.h"
35 #include "../libvlc.h"
44 int vlc_sd_probe_Add (vlc_probe_t
*probe
, const char *name
,
45 const char *longname
, int category
)
47 vlc_sd_probe_t names
= { strdup(name
), strdup(longname
), category
};
49 if (unlikely (names
.name
== NULL
|| names
.longname
== NULL
50 || vlc_probe_add (probe
, &names
, sizeof (names
))))
53 free (names
.longname
);
56 return VLC_PROBE_CONTINUE
;
59 #undef vlc_sd_GetNames
62 * Gets the list of available services discovery plugins.
64 char **vlc_sd_GetNames (vlc_object_t
*obj
, char ***pppsz_longnames
, int **pp_categories
)
67 vlc_sd_probe_t
*tab
= vlc_probe (obj
, "services probe", &count
);
75 char **names
= malloc (sizeof(char *) * (count
+ 1));
76 char **longnames
= malloc (sizeof(char *) * (count
+ 1));
77 int *categories
= malloc(sizeof(int) * (count
+ 1));
79 if (unlikely (names
== NULL
|| longnames
== NULL
|| categories
== NULL
))
81 for( size_t i
= 0; i
< count
; i
++ )
83 names
[i
] = tab
[i
].name
;
84 longnames
[i
] = tab
[i
].longname
;
85 categories
[i
] = tab
[i
].category
;
88 names
[count
] = longnames
[count
] = NULL
;
89 categories
[count
] = 0;
90 *pppsz_longnames
= longnames
;
91 if( pp_categories
) *pp_categories
= categories
;
92 else free( categories
);
97 static void services_discovery_Destructor ( vlc_object_t
*p_obj
);
101 * Basically you just listen to Service discovery event through the
102 * sd's event manager.
103 * That's how the playlist get's Service Discovery information
106 /*******************************************************************//**
107 * Create a Service discovery
108 ***********************************************************************/
109 services_discovery_t
*vlc_sd_Create( vlc_object_t
*p_super
,
112 services_discovery_t
*p_sd
;
114 p_sd
= vlc_custom_create( p_super
, sizeof( *p_sd
), "services discovery" );
117 free(config_ChainCreate( &p_sd
->psz_name
, &p_sd
->p_cfg
, cfg
));
119 vlc_event_manager_t
*em
= &p_sd
->event_manager
;
120 vlc_event_manager_init( em
, 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_ServicesDiscoveryItemRemoveAll
);
124 vlc_event_manager_register_event_type(em
, vlc_ServicesDiscoveryStarted
);
125 vlc_event_manager_register_event_type(em
, vlc_ServicesDiscoveryEnded
);
127 vlc_object_set_destructor( p_sd
, services_discovery_Destructor
);
131 /*******************************************************************//**
132 * Start a Service Discovery
133 ***********************************************************************/
134 bool vlc_sd_Start ( services_discovery_t
* p_sd
)
136 assert(!p_sd
->p_module
);
138 p_sd
->p_module
= module_need( p_sd
, "services_discovery",
139 p_sd
->psz_name
, true );
140 if( p_sd
->p_module
== NULL
)
142 msg_Err( p_sd
, "no suitable services discovery module" );
146 vlc_event_t event
= {
147 .type
= vlc_ServicesDiscoveryStarted
149 vlc_event_send( &p_sd
->event_manager
, &event
);
153 /*******************************************************************//**
154 * Stop a Service Discovery
155 ***********************************************************************/
156 void vlc_sd_Stop ( services_discovery_t
* p_sd
)
158 vlc_event_t event
= {
159 .type
= vlc_ServicesDiscoveryEnded
162 vlc_event_send( &p_sd
->event_manager
, &event
);
164 module_unneed( p_sd
, p_sd
->p_module
);
165 p_sd
->p_module
= NULL
;
168 /*******************************************************************//**
169 * Destroy a Service Discovery
170 ***********************************************************************/
171 void vlc_sd_Destroy( services_discovery_t
*p_sd
)
173 config_ChainDestroy( p_sd
->p_cfg
);
174 free( p_sd
->psz_name
);
175 vlc_object_release( p_sd
);
178 /*******************************************************************//**
179 * Destructor of the Service Discovery
180 ***********************************************************************/
181 static void services_discovery_Destructor ( vlc_object_t
*p_obj
)
183 services_discovery_t
* p_sd
= (services_discovery_t
*)p_obj
;
184 assert(!p_sd
->p_module
); /* Forgot to call Stop */
185 vlc_event_manager_fini( &p_sd
->event_manager
);
188 /*******************************************************************//**
189 * Get the Localized Name
191 * This is useful for interfaces and libVLC
192 ***********************************************************************/
194 services_discovery_GetLocalizedName ( services_discovery_t
* p_sd
)
196 return strdup( module_get_name( p_sd
->p_module
, true ) );
199 /*******************************************************************//**
200 * Getter for the EventManager
202 * You can receive event notification
203 * This is the preferred way to get new items
204 ***********************************************************************/
205 vlc_event_manager_t
*
206 services_discovery_EventManager ( services_discovery_t
* p_sd
)
208 return &p_sd
->event_manager
;
211 /*******************************************************************//**
212 * Remove all items from the Service Discovery listing
213 ***********************************************************************/
215 services_discovery_RemoveAll ( services_discovery_t
* p_sd
)
218 event
.type
= vlc_ServicesDiscoveryItemRemoveAll
;
220 vlc_event_send( &p_sd
->event_manager
, &event
);
223 /*******************************************************************//**
224 * Add an item to the Service Discovery listing
225 ***********************************************************************/
227 services_discovery_AddItem ( services_discovery_t
* p_sd
, input_item_t
* p_item
,
228 const char * psz_category
)
231 event
.type
= vlc_ServicesDiscoveryItemAdded
;
232 event
.u
.services_discovery_item_added
.p_new_item
= p_item
;
233 event
.u
.services_discovery_item_added
.psz_category
= psz_category
;
235 vlc_event_send( &p_sd
->event_manager
, &event
);
238 /*******************************************************************//**
239 * Remove an item from the Service Discovery listing
240 ***********************************************************************/
242 services_discovery_RemoveItem ( services_discovery_t
* p_sd
, input_item_t
* p_item
)
245 event
.type
= vlc_ServicesDiscoveryItemRemoved
;
246 event
.u
.services_discovery_item_removed
.p_item
= p_item
;
248 vlc_event_send( &p_sd
->event_manager
, &event
);
252 * Playlist - Services discovery bridge
255 struct vlc_sd_internal_t
257 /* the playlist items for category and onelevel */
258 playlist_item_t
*p_node
;
259 services_discovery_t
*p_sd
; /**< Loaded service discovery modules */
263 /* A new item has been added to a certain sd */
264 static void playlist_sd_item_added( const vlc_event_t
* p_event
, void * user_data
)
266 input_item_t
* p_input
= p_event
->u
.services_discovery_item_added
.p_new_item
;
267 const char * psz_cat
= p_event
->u
.services_discovery_item_added
.psz_category
;
268 playlist_item_t
* p_parent
= user_data
;
269 playlist_t
* p_playlist
= p_parent
->p_playlist
;
271 msg_Dbg( p_playlist
, "Adding %s in %s",
272 p_input
->psz_name
? p_input
->psz_name
: "(null)",
273 psz_cat
? psz_cat
: "(null)" );
276 /* If p_parent is in root category (this is clearly a hack) and we have a cat */
277 if( !EMPTY_STR(psz_cat
) )
280 playlist_item_t
* p_cat
;
281 p_cat
= playlist_ChildSearchName( p_parent
, psz_cat
);
284 p_cat
= playlist_NodeCreate( p_playlist
, psz_cat
,
285 p_parent
, PLAYLIST_END
, 0, NULL
);
286 p_cat
->i_flags
&= ~PLAYLIST_SKIP_FLAG
;
291 playlist_NodeAddInput( p_playlist
, p_input
, p_parent
,
292 PLAYLIST_APPEND
, PLAYLIST_END
,
297 /* A new item has been removed from a certain sd */
298 static void playlist_sd_item_removed( const vlc_event_t
* p_event
, void * user_data
)
300 input_item_t
* p_input
= p_event
->u
.services_discovery_item_removed
.p_item
;
301 playlist_item_t
* p_sd_node
= user_data
;
302 playlist_t
*p_playlist
= p_sd_node
->p_playlist
;
305 playlist_item_t
*p_item
=
306 playlist_ItemFindFromInputAndRoot( p_playlist
, p_input
,
312 playlist_item_t
*p_parent
= p_item
->p_parent
;
313 /* if the item was added under a category and the category node
314 becomes empty, delete that node as well */
315 if( p_parent
->i_children
> 1 || p_parent
== p_sd_node
)
316 playlist_DeleteItem( p_playlist
, p_item
, true );
318 playlist_NodeDelete( p_playlist
, p_parent
, true, true );
322 /* A request to remove all ideas from SD */
323 static void playlist_sd_item_removeall( const vlc_event_t
* p_event
, void * user_data
)
326 playlist_item_t
* p_sd_node
= user_data
;
327 if( p_sd_node
== NULL
) return;
328 playlist_t
* p_playlist
= p_sd_node
->p_playlist
;
330 playlist_NodeEmpty( p_playlist
, p_sd_node
, true );
334 int playlist_ServicesDiscoveryAdd( playlist_t
*p_playlist
,
335 const char *psz_name
)
337 /* Perform the addition */
338 services_discovery_t
*p_sd
;
340 msg_Dbg( p_playlist
, "adding services_discovery %s...", psz_name
);
341 p_sd
= vlc_sd_Create( VLC_OBJECT(p_playlist
), psz_name
);
345 /* Free in playlist_ServicesDiscoveryRemove */
346 vlc_sd_internal_t
* p_sds
= malloc( sizeof(*p_sds
) );
349 vlc_sd_Destroy( p_sd
);
353 playlist_item_t
*p_node
;
355 /* Look for configuration chain "longname" */
356 const char *psz_longname
= "?";
359 config_chain_t
*cfg
= p_sd
->p_cfg
;
362 if( cfg
->psz_name
&& !strcmp( cfg
->psz_name
, "longname" ) )
364 psz_longname
= cfg
->psz_value
;
372 p_node
= playlist_NodeCreate( p_playlist
, psz_longname
,
373 p_playlist
->p_root
, PLAYLIST_END
, 0, NULL
);
376 vlc_event_manager_t
*em
= services_discovery_EventManager( p_sd
);
377 vlc_event_attach( em
, vlc_ServicesDiscoveryItemAdded
,
378 playlist_sd_item_added
, p_node
);
380 vlc_event_attach( em
, vlc_ServicesDiscoveryItemRemoved
,
381 playlist_sd_item_removed
, p_node
);
383 vlc_event_attach( em
, vlc_ServicesDiscoveryItemRemoveAll
,
384 playlist_sd_item_removeall
, p_node
);
386 if( !vlc_sd_Start( p_sd
) )
388 vlc_sd_Destroy( p_sd
);
394 p_sds
->p_node
= p_node
;
395 p_sds
->psz_name
= strdup( psz_name
);
398 TAB_APPEND( pl_priv(p_playlist
)->i_sds
, pl_priv(p_playlist
)->pp_sds
, p_sds
);
404 int playlist_ServicesDiscoveryRemove( playlist_t
* p_playlist
,
405 const char *psz_name
)
407 playlist_private_t
*priv
= pl_priv( p_playlist
);
408 vlc_sd_internal_t
* p_sds
= NULL
;
411 for( int i
= 0; i
< priv
->i_sds
; i
++ )
413 if( !strcmp( psz_name
, priv
->pp_sds
[i
]->psz_name
) )
415 p_sds
= priv
->pp_sds
[i
];
416 REMOVE_ELEM( priv
->pp_sds
, priv
->i_sds
, i
);
424 msg_Warn( p_playlist
, "discovery %s is not loaded", psz_name
);
428 services_discovery_t
*p_sd
= p_sds
->p_sd
;
433 vlc_event_detach( services_discovery_EventManager( p_sd
),
434 vlc_ServicesDiscoveryItemAdded
,
435 playlist_sd_item_added
,
438 vlc_event_detach( services_discovery_EventManager( p_sd
),
439 vlc_ServicesDiscoveryItemRemoved
,
440 playlist_sd_item_removed
,
443 /* Remove the sd playlist node if it exists */
445 playlist_NodeDelete( p_playlist
, p_sds
->p_node
, true, false );
448 vlc_sd_Destroy( p_sd
);
449 free( p_sds
->psz_name
);
455 bool playlist_IsServicesDiscoveryLoaded( playlist_t
* p_playlist
,
456 const char *psz_name
)
458 playlist_private_t
*priv
= pl_priv( p_playlist
);
462 for( int i
= 0; i
< priv
->i_sds
; i
++ )
464 vlc_sd_internal_t
*sd
= priv
->pp_sds
[i
];
466 if( sd
->psz_name
&& !strcmp( psz_name
, sd
->psz_name
) )
476 int playlist_ServicesDiscoveryControl( playlist_t
*p_playlist
, const char *psz_name
, int i_control
, ... )
478 playlist_private_t
*priv
= pl_priv( p_playlist
);
479 int i_ret
= VLC_EGENERIC
;
483 for( i
= 0; i
< priv
->i_sds
; i
++ )
485 vlc_sd_internal_t
*sd
= priv
->pp_sds
[i
];
486 if( sd
->psz_name
&& !strcmp( psz_name
, sd
->psz_name
) )
489 va_start( args
, i_control
);
490 i_ret
= vlc_sd_control( sd
->p_sd
, i_control
, args
);
496 assert( i
!= priv
->i_sds
);
502 void playlist_ServicesDiscoveryKillAll( playlist_t
*p_playlist
)
504 playlist_private_t
*priv
= pl_priv( p_playlist
);
506 while( priv
->i_sds
> 0 )
507 playlist_ServicesDiscoveryRemove( p_playlist
,
508 priv
->pp_sds
[0]->psz_name
);