media_discoverer.c 17.6 KB
Newer Older
1 2 3
/*****************************************************************************
 * media_discoverer.c: libvlc new API media discoverer functions
 *****************************************************************************
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
4
 * Copyright (C) 2007 VLC authors and VideoLAN
5 6 7 8
 * $Id$
 *
 * Authors: Pierre d'Herbemont <pdherbemont # videolan.org>
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
9 10 11
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
12 13 14 15
 * (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
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
16 17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
18
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
19 20 21
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 23
 *****************************************************************************/

24 25 26 27
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

28
#include <assert.h>
29 30 31 32 33 34 35 36 37 38 39

#include <vlc/libvlc.h>
#include <vlc/libvlc_media.h>
#include <vlc/libvlc_media_list.h>
#include <vlc/libvlc_media_discoverer.h>
#include <vlc/libvlc_events.h>

#include <vlc_services_discovery.h>

#include "libvlc_internal.h"
#include "media_internal.h" // libvlc_media_new_from_input_item()
40
#include "media_list_internal.h" // libvlc_media_list_internal_add_media()
41 42 43 44 45 46 47 48 49 50

struct libvlc_media_discoverer_t
{
    libvlc_event_manager_t * p_event_manager;
    libvlc_instance_t *      p_libvlc_instance;
    services_discovery_t *   p_sd;
    libvlc_media_list_t *    p_mlist;
    bool                     running;
    vlc_dictionary_t         catname_to_submedialist;
};
51 52 53 54

/*
 * Private functions
 */
55

56 57 58 59 60 61 62 63
/**************************************************************************
 *       services_discovery_item_added (Private) (VLC event callback)
 **************************************************************************/

static void services_discovery_item_added( const vlc_event_t * p_event,
                                           void * user_data )
{
    input_item_t * p_item = p_event->u.services_discovery_item_added.p_new_item;
64
    const char * psz_cat = p_event->u.services_discovery_item_added.psz_category;
65
    libvlc_media_t * p_md;
66
    libvlc_media_discoverer_t * p_mdis = user_data;
67
    libvlc_media_list_t * p_mlist = p_mdis->p_mlist;
68

69 70
    p_md = libvlc_media_new_from_input_item( p_mdis->p_libvlc_instance,
                                             p_item );
71

72 73 74 75 76
    /* If we have a category, that mean we have to group the items having
     * that category in a media_list. */
    if( psz_cat )
    {
        p_mlist = vlc_dictionary_value_for_key( &p_mdis->catname_to_submedialist, psz_cat );
77

78 79
        if( p_mlist == kVLCDictionaryNotFound )
        {
80
            libvlc_media_t * p_catmd;
81
            p_catmd = libvlc_media_new_as_node( p_mdis->p_libvlc_instance, psz_cat );
82
            p_mlist = libvlc_media_subitems( p_catmd );
83
            p_mlist->b_read_only = true;
84

85
            /* Insert the newly created mlist in our dictionary */
86
            vlc_dictionary_insert( &p_mdis->catname_to_submedialist, psz_cat, p_mlist );
87

88
            /* Insert the md into the root list */
89
            libvlc_media_list_lock( p_mdis->p_mlist );
90
            libvlc_media_list_internal_add_media( p_mdis->p_mlist, p_catmd );
91
            libvlc_media_list_unlock( p_mdis->p_mlist );
92 93 94

            /* We don't release the mlist cause the dictionary
             * doesn't retain the object. But we release the md. */
95
            libvlc_media_release( p_catmd );
96 97
        }
    }
98 99

    libvlc_media_list_lock( p_mlist );
100
    libvlc_media_list_internal_add_media( p_mlist, p_md );
101
    libvlc_media_list_unlock( p_mlist );
102 103

    libvlc_media_release( p_md );
104 105 106 107 108 109 110 111 112
}

/**************************************************************************
 *       services_discovery_item_removed (Private) (VLC event callback)
 **************************************************************************/

static void services_discovery_item_removed( const vlc_event_t * p_event,
                                             void * user_data )
{
113
    input_item_t * p_item = p_event->u.services_discovery_item_added.p_new_item;
114
    libvlc_media_t * p_md;
115 116
    libvlc_media_discoverer_t * p_mdis = user_data;

117
    int i, count = libvlc_media_list_count( p_mdis->p_mlist );
118 119 120
    libvlc_media_list_lock( p_mdis->p_mlist );
    for( i = 0; i < count; i++ )
    {
121
        p_md = libvlc_media_list_item_at_index( p_mdis->p_mlist, i );
122 123
        if( p_md->p_input_item == p_item )
        {
124
            libvlc_media_list_internal_remove_index( p_mdis->p_mlist, i );
125 126 127 128 129 130
            break;
        }
    }
    libvlc_media_list_unlock( p_mdis->p_mlist );
}

131 132 133 134 135 136
/**************************************************************************
 *       services_discovery_removeall (Private) (VLC event callback)
 **************************************************************************/
static void services_discovery_removeall( const vlc_event_t * p_event,
                                             void * user_data )
{
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
137
    VLC_UNUSED(p_event);
138 139 140 141 142
    libvlc_media_discoverer_t * p_mdis = user_data;

    libvlc_media_list_lock( p_mdis->p_mlist );
    for( int i = 0; i < libvlc_media_list_count( p_mdis->p_mlist ); i++ )
    {
143
        libvlc_media_list_internal_remove_index( p_mdis->p_mlist, i );
144 145 146 147
    }
    libvlc_media_list_unlock( p_mdis->p_mlist );
}

148 149 150 151 152 153 154
/**************************************************************************
 *       services_discovery_started (Private) (VLC event callback)
 **************************************************************************/

static void services_discovery_started( const vlc_event_t * p_event,
                                        void * user_data )
{
155
    VLC_UNUSED(p_event);
156 157
    libvlc_media_discoverer_t * p_mdis = user_data;
    libvlc_event_t event;
158
    p_mdis->running = true;
159 160 161 162 163 164 165 166 167 168 169
    event.type = libvlc_MediaDiscovererStarted;
    libvlc_event_send( p_mdis->p_event_manager, &event );
}

/**************************************************************************
 *       services_discovery_ended (Private) (VLC event callback)
 **************************************************************************/

static void services_discovery_ended( const vlc_event_t * p_event,
                                      void * user_data )
{
170
    VLC_UNUSED(p_event);
171
    libvlc_media_discoverer_t * p_mdis = user_data;
172
    libvlc_media_list_t * p_mlist = p_mdis->p_mlist;
173
    libvlc_event_t event;
174

175
    p_mdis->running = false;
176 177 178 179 180

    libvlc_media_list_lock( p_mlist );
    libvlc_media_list_internal_end_reached( p_mlist );
    libvlc_media_list_unlock( p_mlist );

181 182
    event.type = libvlc_MediaDiscovererEnded;
    libvlc_event_send( p_mdis->p_event_manager, &event );
183 184 185 186 187 188 189 190 191 192
}

/*
 * Public libvlc functions
 */

/**************************************************************************
 *       new (Public)
 **************************************************************************/
libvlc_media_discoverer_t *
193
libvlc_media_discoverer_new( libvlc_instance_t * p_inst, const char * psz_name )
194
{
195 196 197
    /* podcast SD is a hack and only works with custom playlist callbacks. */
    if( !strncasecmp( psz_name, "podcast", 7 ) )
        return NULL;
198

199
    libvlc_media_discoverer_t *p_mdis = malloc(sizeof(*p_mdis));
200
    if( unlikely(!p_mdis) )
201
    {
202
        libvlc_printerr( "Not enough memory" );
203 204 205 206
        return NULL;
    }

    p_mdis->p_libvlc_instance = p_inst;
207
    p_mdis->p_mlist = libvlc_media_list_new( p_inst );
208 209
    p_mdis->p_mlist->b_read_only = true;
    p_mdis->running = false;
210

211 212
    vlc_dictionary_init( &p_mdis->catname_to_submedialist, 0 );

213
    p_mdis->p_event_manager = libvlc_event_manager_new( p_mdis );
214 215 216 217 218
    if( unlikely(p_mdis->p_event_manager == NULL) )
    {
        free( p_mdis );
        return NULL;
    }
219

220 221 222
    p_mdis->p_sd = vlc_sd_Create( (vlc_object_t*)p_inst->p_libvlc_int,
                                  psz_name );
    if( unlikely(p_mdis->p_sd == NULL) )
223
    {
224
        libvlc_printerr( "%s: no such discovery module found", psz_name );
225 226
        libvlc_media_list_release( p_mdis->p_mlist );
        libvlc_event_manager_release( p_mdis->p_event_manager );
227
        free( p_mdis );
228 229 230 231 232 233 234 235 236 237 238
        return NULL;
    }

    vlc_event_attach( services_discovery_EventManager( p_mdis->p_sd ),
                      vlc_ServicesDiscoveryItemAdded,
                      services_discovery_item_added,
                      p_mdis );
    vlc_event_attach( services_discovery_EventManager( p_mdis->p_sd ),
                      vlc_ServicesDiscoveryItemRemoved,
                      services_discovery_item_removed,
                      p_mdis );
239 240 241 242 243 244 245 246
    vlc_event_attach( services_discovery_EventManager( p_mdis->p_sd ),
                      vlc_ServicesDiscoveryStarted,
                      services_discovery_started,
                      p_mdis );
    vlc_event_attach( services_discovery_EventManager( p_mdis->p_sd ),
                      vlc_ServicesDiscoveryEnded,
                      services_discovery_ended,
                      p_mdis );
247 248 249 250
    vlc_event_attach( services_discovery_EventManager( p_mdis->p_sd ),
                      vlc_ServicesDiscoveryItemRemoveAll,
                      services_discovery_removeall,
                      p_mdis );
251

252
    libvlc_retain( p_inst );
253 254 255 256 257 258 259 260 261
    return p_mdis;
}

/**************************************************************************
 *       start (Public)
 **************************************************************************/
LIBVLC_API int
libvlc_media_discoverer_start( libvlc_media_discoverer_t * p_mdis )
{
262
    /* Here we go */
263 264 265 266 267 268 269 270 271
    return vlc_sd_Start( p_mdis->p_sd ) ? 0 : -1;
}

/**************************************************************************
 *       stop (Public)
 **************************************************************************/
LIBVLC_API void
libvlc_media_discoverer_stop( libvlc_media_discoverer_t * p_mdis )
{
272
    vlc_sd_Stop( p_mdis->p_sd );
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
}

/**************************************************************************
 *       new_from_name (Public)
 *
 * \deprecated Use libvlc_media_discoverer_new and libvlc_media_discoverer_start
 **************************************************************************/
libvlc_media_discoverer_t *
libvlc_media_discoverer_new_from_name( libvlc_instance_t * p_inst,
                                       const char * psz_name )
{
    libvlc_media_discoverer_t *p_mdis = libvlc_media_discoverer_new( p_inst, psz_name );

    if( !p_mdis )
        return NULL;

    if( libvlc_media_discoverer_start( p_mdis ) != 0)
290
    {
291
        libvlc_media_discoverer_release( p_mdis );
292 293
        return NULL;
    }
294 295 296 297 298 299 300 301 302 303

    return p_mdis;
}

/**************************************************************************
 * release (Public)
 **************************************************************************/
void
libvlc_media_discoverer_release( libvlc_media_discoverer_t * p_mdis )
{
304 305
    int i;

306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
    vlc_event_detach( services_discovery_EventManager( p_mdis->p_sd ),
                     vlc_ServicesDiscoveryItemAdded,
                     services_discovery_item_added,
                     p_mdis );
    vlc_event_detach( services_discovery_EventManager( p_mdis->p_sd ),
                     vlc_ServicesDiscoveryItemRemoved,
                     services_discovery_item_removed,
                     p_mdis );
    vlc_event_detach( services_discovery_EventManager( p_mdis->p_sd ),
                     vlc_ServicesDiscoveryStarted,
                     services_discovery_started,
                     p_mdis );
    vlc_event_detach( services_discovery_EventManager( p_mdis->p_sd ),
                     vlc_ServicesDiscoveryEnded,
                     services_discovery_ended,
                     p_mdis );
322 323 324 325
    vlc_event_detach( services_discovery_EventManager( p_mdis->p_sd ),
                     vlc_ServicesDiscoveryItemRemoveAll,
                     services_discovery_removeall,
                     p_mdis );
326

327
    libvlc_media_list_release( p_mdis->p_mlist );
328

329 330 331 332
    if( p_mdis->running )
        vlc_sd_Stop( p_mdis->p_sd );

    vlc_sd_Destroy( p_mdis->p_sd );
333 334 335 336 337 338 339 340 341 342 343

    /* Free catname_to_submedialist and all the mlist */
    char ** all_keys = vlc_dictionary_all_keys( &p_mdis->catname_to_submedialist );
    for( i = 0; all_keys[i]; i++ )
    {
        libvlc_media_list_t * p_catmlist = vlc_dictionary_value_for_key( &p_mdis->catname_to_submedialist, all_keys[i] );
        libvlc_media_list_release( p_catmlist );
        free( all_keys[i] );
    }
    free( all_keys );

344
    vlc_dictionary_clear( &p_mdis->catname_to_submedialist, NULL, NULL );
345
    libvlc_event_manager_release( p_mdis->p_event_manager );
346
    libvlc_release( p_mdis->p_libvlc_instance );
347

348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365
    free( p_mdis );
}

/**************************************************************************
 * localized_name (Public)
 **************************************************************************/
char *
libvlc_media_discoverer_localized_name( libvlc_media_discoverer_t * p_mdis )
{
    return services_discovery_GetLocalizedName( p_mdis->p_sd );
}

/**************************************************************************
 * media_list (Public)
 **************************************************************************/
libvlc_media_list_t *
libvlc_media_discoverer_media_list( libvlc_media_discoverer_t * p_mdis )
{
366 367
    libvlc_media_list_retain( p_mdis->p_mlist );
    return p_mdis->p_mlist;
368 369
}

370 371 372 373 374 375 376 377 378
/**************************************************************************
 * event_manager (Public)
 **************************************************************************/
libvlc_event_manager_t *
libvlc_media_discoverer_event_manager( libvlc_media_discoverer_t * p_mdis )
{
    return p_mdis->p_event_manager;
}

379 380 381 382

/**************************************************************************
 * running (Public)
 **************************************************************************/
383
int
384 385 386 387
libvlc_media_discoverer_is_running( libvlc_media_discoverer_t * p_mdis )
{
    return p_mdis->running;
}
388 389

void
390 391
libvlc_media_discoverer_list_release( libvlc_media_discoverer_description **pp_services,
                                      unsigned int i_count )
392 393 394 395 396 397 398 399 400 401 402 403 404 405
{
    if( i_count > 0 )
    {
        for( unsigned int i = 0; i < i_count; ++i )
        {
            free( pp_services[i]->psz_name );
            free( pp_services[i]->psz_longname );
        }
        free( *pp_services );
        free( pp_services );
    }
}

unsigned int
406 407 408
libvlc_media_discoverer_list_get( libvlc_instance_t *p_inst,
                                  libvlc_media_discoverer_category i_cat,
                                  libvlc_media_discoverer_description ***ppp_services )
409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454
{
    assert( p_inst != NULL && ppp_services != NULL );

    int i_core_cat;
    switch( i_cat )
    {
    case libvlc_media_discoverer_devices:
        i_core_cat = SD_CAT_DEVICES;
        break;
    case libvlc_media_discoverer_lan:
        i_core_cat = SD_CAT_LAN;
        break;
    case libvlc_media_discoverer_podcasts:
        i_core_cat = SD_CAT_INTERNET;
        break;
    case libvlc_media_discoverer_localdirs:
        i_core_cat = SD_CAT_MYCOMPUTER;
        break;
    default:
        vlc_assert_unreachable();
        *ppp_services = NULL;
        return 0;
    }

    /* Fetch all sd names, longnames and categories */
    char **ppsz_names, **ppsz_longnames;
    int *p_categories;
    ppsz_names = vlc_sd_GetNames( p_inst->p_libvlc_int, &ppsz_longnames,
                                  &p_categories );

    if( ppsz_names == NULL )
    {
        *ppp_services = NULL;
        return 0;
    }

    /* Count the number of sd matching our category (i_cat/i_core_cat) */
    unsigned int i_nb_services = 0;
    char **ppsz_name = ppsz_names;
    int *p_category = p_categories;
    for( ; *ppsz_name != NULL; ppsz_name++, p_category++ )
    {
        if( *p_category == i_core_cat )
            i_nb_services++;
    }

455
    libvlc_media_discoverer_description **pp_services = NULL, *p_services = NULL;
456 457 458 459 460 461 462
    if( i_nb_services > 0 )
    {
        /* Double alloc here, so that the caller iterates through pointers of
         * struct instead of structs. This allows us to modify the struct
         * without breaking the API. */

        pp_services = malloc( i_nb_services
463
                              * sizeof(libvlc_media_discoverer_description *) );
464
        p_services = malloc( i_nb_services
465
                             * sizeof(libvlc_media_discoverer_description) );
466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482
        if( pp_services == NULL || p_services == NULL )
        {
            free( pp_services );
            free( p_services );
            pp_services = NULL;
            p_services = NULL;
            i_nb_services = 0;
            /* Even if alloc fails, the next loop must be run in order to free
             * names returned by vlc_sd_GetNames */
        }
    }

    /* Fill output pp_services or free unused name, longname */
    char **ppsz_longname = ppsz_longnames;
    ppsz_name = ppsz_names;
    p_category = p_categories;
    unsigned int i_service_idx = 0;
483
    libvlc_media_discoverer_description *p_service = p_services;
484
    for( ; *ppsz_name != NULL; ppsz_name++, ppsz_longname++, p_category++ )
485 486 487 488 489 490
    {
        if( pp_services != NULL && *p_category == i_core_cat )
        {
            p_service->psz_name = *ppsz_name;
            p_service->psz_longname = *ppsz_longname;
            p_service->i_cat = i_cat;
491
            pp_services[i_service_idx++] = p_service++;
492 493 494 495 496 497 498 499 500 501 502 503 504 505
        }
        else
        {
            free( *ppsz_name );
            free( *ppsz_longname );
        }
    }
    free( ppsz_names );
    free( ppsz_longnames );
    free( p_categories );

    *ppp_services = pp_services;
    return i_nb_services;
}