services_discovery.c 14.7 KB
Newer Older
zorglub's avatar
zorglub committed
1
2
3
/*****************************************************************************
 * services_discovery.c : Manage playlist services_discovery modules
 *****************************************************************************
4
 * Copyright (C) 1999-2004 the VideoLAN team
dionoea's avatar
dionoea committed
5
 * $Id$
zorglub's avatar
zorglub committed
6
 *
7
 * Authors: Clément Stenac <zorglub@videolan.org>
zorglub's avatar
zorglub committed
8
9
10
11
12
13
14
15
16
17
18
19
20
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
dionoea's avatar
dionoea committed
21
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
zorglub's avatar
zorglub committed
22
 *****************************************************************************/
23
24
25
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
26
#include <assert.h>
27

28
#include <vlc_common.h>
zorglub's avatar
zorglub committed
29
#include "vlc_playlist.h"
30
#include "vlc_events.h"
31
#include "playlist_internal.h"
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
32
#include "../libvlc.h"
zorglub's avatar
zorglub committed
33
34
35

static void RunSD( services_discovery_t *p_sd );

36
37
38
39
40
41
/*
 * 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
 */
42

43
44
45
/***********************************************************************
 * GetServicesNames
 ***********************************************************************/
46
47
char ** __services_discovery_GetServicesNames( vlc_object_t * p_super,
                                               char ***pppsz_longnames )
48
{
49
50
    return module_GetModulesNamesForCapability( p_super, "services_discovery",
                                                pppsz_longnames );
51
52
}

53
54
55
56
57
58
/***********************************************************************
 * Create
 ***********************************************************************/
services_discovery_t *
services_discovery_Create ( vlc_object_t * p_super, const char * psz_module_name )
{
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
59
60
61
    services_discovery_t *p_sd;
    p_sd = vlc_custom_create( p_super, sizeof( *p_sd ), VLC_OBJECT_GENERIC,
                              "services discovery" );
62
63
    if( !p_sd )
        return NULL;
64

65
66
67
68
    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 );
69
    vlc_event_manager_register_event_type( &p_sd->event_manager,
70
            vlc_ServicesDiscoveryItemAdded );
71
    vlc_event_manager_register_event_type( &p_sd->event_manager,
72
            vlc_ServicesDiscoveryItemRemoved );
73
74
75
76
    vlc_event_manager_register_event_type( &p_sd->event_manager,
            vlc_ServicesDiscoveryStarted );
    vlc_event_manager_register_event_type( &p_sd->event_manager,
            vlc_ServicesDiscoveryEnded );
77

78
    p_sd->p_module = module_Need( p_sd, "services_discovery", psz_module_name, true );
79

80
81
82
    if( p_sd->p_module == NULL )
    {
        msg_Err( p_super, "no suitable services discovery module" );
83
        vlc_object_release( p_sd );
84
85
86
        return NULL;
    }
    p_sd->psz_module = strdup( psz_module_name );
87
    p_sd->b_die = false; /* FIXME */
88

89
    vlc_object_attach( p_sd, p_super );
90
91
92
93
94
95
96
97
    return p_sd;
}

/***********************************************************************
 * Destroy
 ***********************************************************************/
void services_discovery_Destroy ( services_discovery_t * p_sd )
{
98
    vlc_event_manager_fini( &p_sd->event_manager );
99
100
101
102

    free( p_sd->psz_module );
    free( p_sd->psz_localized_name );

103
    vlc_object_detach( p_sd );
104
    vlc_object_release( p_sd );
105
106
107
108
109
110
111
112
113
}

/***********************************************************************
 * Start
 ***********************************************************************/
int services_discovery_Start ( services_discovery_t * p_sd )
{
    if ((p_sd->pf_run != NULL)
        && vlc_thread_create( p_sd, "services_discovery", RunSD,
114
                              VLC_THREAD_PRIORITY_LOW, false))
115
116
    {
        msg_Err( p_sd, "cannot create services discovery thread" );
117
        vlc_object_release( p_sd );
118
119
120
121
122
        return VLC_EGENERIC;
    }
    return VLC_SUCCESS;
}

123
124
125
126
127
128
129
130
131
132
133
/***********************************************************************
 * Stop
 ***********************************************************************/
void services_discovery_Stop ( services_discovery_t * p_sd )
{
    vlc_object_kill( p_sd );
    if( p_sd->pf_run ) vlc_thread_join( p_sd );

    module_Unneed( p_sd, p_sd->p_module );
}

134
135
136
/***********************************************************************
 * GetLocalizedName
 ***********************************************************************/
137
char *
138
139
140
141
142
143
144
145
146
147
148
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 )
{
149
    free( p_sd->psz_localized_name );
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
    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( services_discovery_t *p_sd )
{
195
196
197
198
199
    vlc_event_t event;

    event.type = vlc_ServicesDiscoveryStarted;
    vlc_event_send( &p_sd->event_manager, &event );

200
    p_sd->pf_run( p_sd );
201
202
203

    event.type = vlc_ServicesDiscoveryEnded;
    vlc_event_send( &p_sd->event_manager, &event );
204
205
206
207
208
209
    return;
}

/*
 * Playlist - Services discovery bridge
 */
210

211
212
213
214
215
216
217
 /* A new item has been added to a certain sd */
static void playlist_sd_item_added( const vlc_event_t * p_event, void * user_data )
{
    input_item_t * p_input = p_event->u.services_discovery_item_added.p_new_item;
    const char * psz_cat = p_event->u.services_discovery_item_added.psz_category;
    playlist_item_t *p_new_item, * p_parent = user_data;

218
219
220
    msg_Dbg( p_parent->p_playlist, "Adding %s in %s",
                p_input->psz_name ? p_input->psz_name : "(null)",
                psz_cat ? psz_cat : "(null)" );
221

222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
    /* If p_parent is in root category (this is clearly a hack) and we have a cat */
    if( !EMPTY_STR(psz_cat) &&
        p_parent->p_parent == p_parent->p_playlist->p_root_category )
    {
        /* */
        playlist_item_t * p_cat;
        p_cat = playlist_ChildSearchName( p_parent, psz_cat );
        if( !p_cat )
        {
            p_cat = playlist_NodeCreate( p_parent->p_playlist, psz_cat,
                                         p_parent, 0, NULL );
            p_cat->i_flags &= ~PLAYLIST_SKIP_FLAG;
        }
        p_parent = p_cat;
    }

238
    p_new_item = playlist_NodeAddInput( p_parent->p_playlist, p_input, p_parent,
239
                                        PLAYLIST_APPEND, PLAYLIST_END, false );
240
241
242
243
244
    if( p_new_item )
    {
        p_new_item->i_flags &= ~PLAYLIST_SKIP_FLAG;
        p_new_item->i_flags &= ~PLAYLIST_SAVE_FLAG;
    }
245
246
247
248
249
}

 /* A new item has been removed from a certain sd */
static void playlist_sd_item_removed( const vlc_event_t * p_event, void * user_data )
{
250
251
    input_item_t * p_input = p_event->u.services_discovery_item_removed.p_item;
    playlist_item_t * p_parent = user_data;
252
    playlist_item_t * p_pl_item;
253

254
255
256
257
    /* First make sure that if item is a node it will be deleted.
     * XXX: Why don't we have a function to ensure that in the playlist code ? */
    vlc_object_lock( p_parent->p_playlist );
    p_pl_item = playlist_ItemFindFromInputAndRoot( p_parent->p_playlist,
258
            p_input->i_id, p_parent, false );
259

260
    if( p_pl_item && p_pl_item->i_children > -1 )
261
    {
262
        playlist_NodeDelete( p_parent->p_playlist, p_pl_item, true, false );
263
264
265
266
        vlc_object_unlock( p_parent->p_playlist );
        return;
    }
    vlc_object_unlock( p_parent->p_playlist );
267

268
    /* Delete the non-node item normally */
269
    playlist_DeleteInputInParent( p_parent->p_playlist, p_input->i_id,
270
                                  p_parent, false );
271
272
}

273
int playlist_ServicesDiscoveryAdd( playlist_t *p_playlist,  const char *psz_modules )
zorglub's avatar
zorglub committed
274
{
275
276
277
278
    const char *psz_parser = psz_modules ?: "";
    int retval = VLC_SUCCESS;

    for (;;)
zorglub's avatar
zorglub committed
279
    {
280
        struct playlist_services_discovery_support_t * p_sds;
281
282
283
        playlist_item_t * p_cat;
        playlist_item_t * p_one;

284
285
        while( *psz_parser == ' ' || *psz_parser == ':' || *psz_parser == ',' )
            psz_parser++;
zorglub's avatar
zorglub committed
286

287
288
        if( *psz_parser == '\0' )
            break;
zorglub's avatar
zorglub committed
289

290
291
292
        const char *psz_next = strchr( psz_parser, ':' );
        if( psz_next == NULL )
            psz_next = psz_parser + strlen( psz_parser );
293

294
295
296
297
        char psz_plugin[psz_next - psz_parser + 1];
        memcpy (psz_plugin, psz_parser, sizeof (psz_plugin) - 1);
        psz_plugin[sizeof (psz_plugin) - 1] = '\0';
        psz_parser = psz_next;
298

299
300
        /* Perform the addition */
        msg_Dbg( p_playlist, "Add services_discovery %s", psz_plugin );
301
        services_discovery_t *p_sd;
302

303
304
305
        p_sd = services_discovery_Create( (vlc_object_t*)p_playlist, psz_plugin );
        if( !p_sd )
            continue;
306

307
        char * psz = services_discovery_GetLocalizedName( p_sd );
308
309
        assert( psz );
        playlist_NodesPairCreate( p_playlist, psz,
310
                &p_cat, &p_one, false );
311
        free( psz );
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333

        vlc_event_attach( services_discovery_EventManager( p_sd ),
                          vlc_ServicesDiscoveryItemAdded,
                          playlist_sd_item_added,
                          p_one );

        vlc_event_attach( services_discovery_EventManager( p_sd ),
                          vlc_ServicesDiscoveryItemAdded,
                          playlist_sd_item_added,
                          p_cat );

        vlc_event_attach( services_discovery_EventManager( p_sd ),
                          vlc_ServicesDiscoveryItemRemoved,
                          playlist_sd_item_removed,
                          p_one );

        vlc_event_attach( services_discovery_EventManager( p_sd ),
                          vlc_ServicesDiscoveryItemRemoved,
                          playlist_sd_item_removed,
                          p_cat );

        services_discovery_Start( p_sd );
334

335
        /* Free in playlist_ServicesDiscoveryRemove */
336
337
338
339
340
341
        p_sds = malloc( sizeof(struct playlist_services_discovery_support_t) );
        if( !p_sds )
        {
            msg_Err( p_playlist, "No more memory" );
            return VLC_ENOMEM;
        }
342
343
344
        p_sds->p_sd = p_sd;
        p_sds->p_one = p_one;
        p_sds->p_cat = p_cat;
345

346
        PL_LOCK;
347
        TAB_APPEND( p_playlist->i_sds, p_playlist->pp_sds, p_sds );
348
        PL_UNLOCK;
zorglub's avatar
zorglub committed
349
    }
350
351

    return retval;
zorglub's avatar
zorglub committed
352
353
}

zorglub's avatar
zorglub committed
354
int playlist_ServicesDiscoveryRemove( playlist_t * p_playlist,
zorglub's avatar
zorglub committed
355
356
                                       const char *psz_module )
{
357
    struct playlist_services_discovery_support_t * p_sds = NULL;
zorglub's avatar
zorglub committed
358
359
    int i;

360
    PL_LOCK;
361
    for( i = 0 ; i< p_playlist->i_sds ; i ++ )
zorglub's avatar
zorglub committed
362
    {
363
        if( !strcmp( psz_module, p_playlist->pp_sds[i]->p_sd->psz_module ) )
zorglub's avatar
zorglub committed
364
        {
365
366
            p_sds = p_playlist->pp_sds[i];
            REMOVE_ELEM( p_playlist->pp_sds, p_playlist->i_sds, i );
zorglub's avatar
zorglub committed
367
368
369
            break;
        }
    }
370
    PL_UNLOCK;
zorglub's avatar
zorglub committed
371

372
    if( !p_sds || !p_sds->p_sd )
zorglub's avatar
zorglub committed
373
    {
374
375
376
        msg_Warn( p_playlist, "module %s is not loaded", psz_module );
        return VLC_EGENERIC;
    }
377

378
    services_discovery_Stop( p_sds->p_sd );
379

380
    vlc_event_detach( services_discovery_EventManager( p_sds->p_sd ),
381
382
                        vlc_ServicesDiscoveryItemAdded,
                        playlist_sd_item_added,
383
                        p_sds->p_one );
384

385
    vlc_event_detach( services_discovery_EventManager( p_sds->p_sd ),
386
387
                        vlc_ServicesDiscoveryItemAdded,
                        playlist_sd_item_added,
388
                        p_sds->p_cat );
389

390
    vlc_event_detach( services_discovery_EventManager( p_sds->p_sd ),
391
392
                        vlc_ServicesDiscoveryItemRemoved,
                        playlist_sd_item_removed,
393
                        p_sds->p_one );
394

395
    vlc_event_detach( services_discovery_EventManager( p_sds->p_sd ),
396
397
                        vlc_ServicesDiscoveryItemRemoved,
                        playlist_sd_item_removed,
398
                        p_sds->p_cat );
399

400
401
    /* Remove the sd playlist node if it exists */
    PL_LOCK;
402
403
    if( p_sds->p_cat != p_playlist->p_root_category &&
        p_sds->p_one != p_playlist->p_root_onelevel )
zorglub's avatar
zorglub committed
404
    {
405
406
        playlist_NodeDelete( p_playlist, p_sds->p_cat, true, false );
        playlist_NodeDelete( p_playlist, p_sds->p_one, true, false );
zorglub's avatar
zorglub committed
407
    }
408
409
    PL_UNLOCK;

410
    services_discovery_Destroy( p_sds->p_sd );
411

zorglub's avatar
zorglub committed
412
    return VLC_SUCCESS;
zorglub's avatar
zorglub committed
413
414
}

415
bool playlist_IsServicesDiscoveryLoaded( playlist_t * p_playlist,
416
417
418
                                              const char *psz_module )
{
    int i;
419
    PL_LOCK;
420

421
    for( i = 0 ; i< p_playlist->i_sds ; i ++ )
422
    {
423
        if( !strcmp( psz_module, p_playlist->pp_sds[i]->p_sd->psz_module ) )
424
        {
425
            PL_UNLOCK;
426
            return true;
427
428
        }
    }
429
    PL_UNLOCK;
430
    return false;
431
432
}

433
434
435
436
437
438
439
void playlist_ServicesDiscoveryKillAll( playlist_t *p_playlist )
{
    while( p_playlist->i_sds > 0 )
        playlist_ServicesDiscoveryRemove( p_playlist,
                                     p_playlist->pp_sds[0]->p_sd->psz_module );
}