+#include "../libvlc.h"
+
+static void* RunSD( vlc_object_t *p_this );
+
+/*
+ * Services discovery
+ * Basically you just listen to Service discovery event through the
+ * sd's event manager.
+ * That's how the playlist get's Service Discovery information
+ */
+
+/***********************************************************************
+ * GetServicesNames
+ ***********************************************************************/
+char ** __services_discovery_GetServicesNames( vlc_object_t * p_super,
+ char ***pppsz_longnames )
+{
+ return module_GetModulesNamesForCapability( p_super, "services_discovery",
+ pppsz_longnames );
+}
+
+/***********************************************************************
+ * Create
+ ***********************************************************************/
+services_discovery_t *
+services_discovery_Create ( vlc_object_t * p_super, const char * psz_module_name )
+{
+ services_discovery_t *p_sd;
+ p_sd = vlc_custom_create( p_super, sizeof( *p_sd ), VLC_OBJECT_GENERIC,
+ "services discovery" );
+ if( !p_sd )
+ return NULL;
+
+ p_sd->pf_run = NULL;
+ p_sd->psz_localized_name = NULL;
+
+ vlc_event_manager_init( &p_sd->event_manager, p_sd, (vlc_object_t *)p_sd );
+ vlc_event_manager_register_event_type( &p_sd->event_manager,
+ vlc_ServicesDiscoveryItemAdded );
+ vlc_event_manager_register_event_type( &p_sd->event_manager,
+ vlc_ServicesDiscoveryItemRemoved );
+ vlc_event_manager_register_event_type( &p_sd->event_manager,
+ vlc_ServicesDiscoveryStarted );
+ vlc_event_manager_register_event_type( &p_sd->event_manager,
+ vlc_ServicesDiscoveryEnded );
+
+ p_sd->p_module = module_Need( p_sd, "services_discovery", psz_module_name, true );
+
+ if( p_sd->p_module == NULL )
+ {
+ msg_Err( p_super, "no suitable services discovery module" );
+ vlc_object_release( p_sd );
+ return NULL;
+ }
+ p_sd->psz_module = strdup( psz_module_name );
+ p_sd->b_die = false; /* FIXME */
+
+ vlc_object_attach( p_sd, p_super );
+ return p_sd;
+}
+
+/***********************************************************************
+ * Destroy
+ ***********************************************************************/
+void services_discovery_Destroy ( services_discovery_t * p_sd )
+{
+ vlc_event_manager_fini( &p_sd->event_manager );
+
+ free( p_sd->psz_module );
+ free( p_sd->psz_localized_name );
+
+ vlc_object_detach( p_sd );
+ vlc_object_release( p_sd );
+}
+
+/***********************************************************************
+ * Start
+ ***********************************************************************/
+int services_discovery_Start ( services_discovery_t * p_sd )
+{
+ if ((p_sd->pf_run != NULL)
+ && vlc_thread_create( p_sd, "services_discovery", RunSD,
+ VLC_THREAD_PRIORITY_LOW, false))
+ {
+ msg_Err( p_sd, "cannot create services discovery thread" );
+ vlc_object_release( p_sd );
+ return VLC_EGENERIC;
+ }
+ return VLC_SUCCESS;
+}
+
+/***********************************************************************
+ * Stop
+ ***********************************************************************/
+static void ObjectKillChildrens( vlc_object_t *p_obj )
+{
+ vlc_list_t *p_list;
+ int i;
+ vlc_object_kill( p_obj );
+
+ p_list = vlc_list_children( p_obj );
+ for( i = 0; i < p_list->i_count; i++ )
+ ObjectKillChildrens( p_list->p_values[i].p_object );
+ vlc_list_release( p_list );
+}
+
+void services_discovery_Stop ( services_discovery_t * p_sd )
+{
+ ObjectKillChildrens( VLC_OBJECT(p_sd) );
+ if( p_sd->pf_run ) vlc_thread_join( p_sd );
+
+ module_Unneed( p_sd, p_sd->p_module );
+}
+
+/***********************************************************************
+ * GetLocalizedName
+ ***********************************************************************/
+char *
+services_discovery_GetLocalizedName ( services_discovery_t * p_sd )
+{
+ return p_sd->psz_localized_name ? strdup( p_sd->psz_localized_name ) : NULL;
+}
+
+/***********************************************************************
+ * SetLocalizedName
+ ***********************************************************************/
+void
+services_discovery_SetLocalizedName ( services_discovery_t * p_sd, const char *psz )
+{
+ free( p_sd->psz_localized_name );
+ p_sd->psz_localized_name = strdup(psz);
+}
+
+/***********************************************************************
+ * EventManager
+ ***********************************************************************/
+vlc_event_manager_t *
+services_discovery_EventManager ( services_discovery_t * p_sd )
+{
+ return &p_sd->event_manager;
+}
+
+/***********************************************************************
+ * AddItem
+ ***********************************************************************/
+void
+services_discovery_AddItem ( services_discovery_t * p_sd, input_item_t * p_item,
+ const char * psz_category )
+{
+ vlc_event_t event;
+ event.type = vlc_ServicesDiscoveryItemAdded;
+ event.u.services_discovery_item_added.p_new_item = p_item;
+ event.u.services_discovery_item_added.psz_category = psz_category;
+
+ vlc_event_send( &p_sd->event_manager, &event );
+}
+
+/***********************************************************************
+ * RemoveItem
+ ***********************************************************************/
+void
+services_discovery_RemoveItem ( services_discovery_t * p_sd, input_item_t * p_item )
+{
+ vlc_event_t event;
+ event.type = vlc_ServicesDiscoveryItemRemoved;
+ event.u.services_discovery_item_removed.p_item = p_item;
+
+ vlc_event_send( &p_sd->event_manager, &event );
+}
+
+/***********************************************************************
+ * RunSD (Private)
+ ***********************************************************************/
+static void* RunSD( vlc_object_t *p_this )
+{
+ services_discovery_t *p_sd = (services_discovery_t *)p_this;
+ vlc_event_t event = {
+ .type = vlc_ServicesDiscoveryStarted
+ };
+ int canc = vlc_savecancel ();
+
+ vlc_event_send( &p_sd->event_manager, &event );
+
+ p_sd->pf_run( p_sd );
+
+ event.type = vlc_ServicesDiscoveryEnded;
+ vlc_event_send( &p_sd->event_manager, &event );
+ vlc_restorecancel (canc);
+ return NULL;
+}