media.c 41.5 KB
Newer Older
1
/*****************************************************************************
2
 * media.c: Libvlc API media descripor management
3
 *****************************************************************************
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
 *****************************************************************************/

Geoffroy Couprie's avatar
Geoffroy Couprie committed
24 25 26 27
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

28
#include <assert.h>
29
#include <errno.h>
30

31
#include <vlc/libvlc.h>
32 33 34 35 36
#include <vlc/libvlc_media.h>
#include <vlc/libvlc_media_list.h> // For the subitems, here for convenience
#include <vlc/libvlc_events.h>

#include <vlc_common.h>
37 38
#include <vlc_input.h>
#include <vlc_meta.h>
39
#include <vlc_playlist.h> /* For the preparser */
40
#include <vlc_url.h>
41

42
#include "../src/libvlc.h"
43 44 45

#include "libvlc_internal.h"
#include "media_internal.h"
46
#include "media_list_internal.h"
47

48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
static const vlc_meta_type_t libvlc_to_vlc_meta[] =
{
    [libvlc_meta_Title]        = vlc_meta_Title,
    [libvlc_meta_Artist]       = vlc_meta_Artist,
    [libvlc_meta_Genre]        = vlc_meta_Genre,
    [libvlc_meta_Copyright]    = vlc_meta_Copyright,
    [libvlc_meta_Album]        = vlc_meta_Album,
    [libvlc_meta_TrackNumber]  = vlc_meta_TrackNumber,
    [libvlc_meta_Description]  = vlc_meta_Description,
    [libvlc_meta_Rating]       = vlc_meta_Rating,
    [libvlc_meta_Date]         = vlc_meta_Date,
    [libvlc_meta_Setting]      = vlc_meta_Setting,
    [libvlc_meta_URL]          = vlc_meta_URL,
    [libvlc_meta_Language]     = vlc_meta_Language,
    [libvlc_meta_NowPlaying]   = vlc_meta_NowPlaying,
    [libvlc_meta_Publisher]    = vlc_meta_Publisher,
    [libvlc_meta_EncodedBy]    = vlc_meta_EncodedBy,
    [libvlc_meta_ArtworkURL]   = vlc_meta_ArtworkURL,
66 67 68 69 70 71
    [libvlc_meta_TrackID]      = vlc_meta_TrackID,
    [libvlc_meta_TrackTotal]   = vlc_meta_TrackTotal,
    [libvlc_meta_Director]     = vlc_meta_Director,
    [libvlc_meta_Season]       = vlc_meta_Season,
    [libvlc_meta_Episode]      = vlc_meta_Episode,
    [libvlc_meta_ShowName]     = vlc_meta_ShowName,
72
    [libvlc_meta_Actors]       = vlc_meta_Actors,
73
    [libvlc_meta_AlbumArtist]  = vlc_meta_AlbumArtist,
74 75
    [libvlc_meta_DiscNumber]   = vlc_meta_DiscNumber,
    [libvlc_meta_DiscTotal]    = vlc_meta_DiscTotal
76 77
};

78
static const libvlc_meta_t vlc_to_libvlc_meta[] =
79 80 81 82 83 84 85 86 87 88 89 90 91 92
{
    [vlc_meta_Title]        = libvlc_meta_Title,
    [vlc_meta_Artist]       = libvlc_meta_Artist,
    [vlc_meta_Genre]        = libvlc_meta_Genre,
    [vlc_meta_Copyright]    = libvlc_meta_Copyright,
    [vlc_meta_Album]        = libvlc_meta_Album,
    [vlc_meta_TrackNumber]  = libvlc_meta_TrackNumber,
    [vlc_meta_Description]  = libvlc_meta_Description,
    [vlc_meta_Rating]       = libvlc_meta_Rating,
    [vlc_meta_Date]         = libvlc_meta_Date,
    [vlc_meta_Setting]      = libvlc_meta_Setting,
    [vlc_meta_URL]          = libvlc_meta_URL,
    [vlc_meta_Language]     = libvlc_meta_Language,
    [vlc_meta_NowPlaying]   = libvlc_meta_NowPlaying,
93
    [vlc_meta_ESNowPlaying] = libvlc_meta_NowPlaying,
94 95 96
    [vlc_meta_Publisher]    = libvlc_meta_Publisher,
    [vlc_meta_EncodedBy]    = libvlc_meta_EncodedBy,
    [vlc_meta_ArtworkURL]   = libvlc_meta_ArtworkURL,
97 98 99 100 101 102
    [vlc_meta_TrackID]      = libvlc_meta_TrackID,
    [vlc_meta_TrackTotal]   = libvlc_meta_TrackTotal,
    [vlc_meta_Director]     = libvlc_meta_Director,
    [vlc_meta_Season]       = libvlc_meta_Season,
    [vlc_meta_Episode]      = libvlc_meta_Episode,
    [vlc_meta_ShowName]     = libvlc_meta_ShowName,
103
    [vlc_meta_Actors]       = libvlc_meta_Actors,
104
    [vlc_meta_AlbumArtist]  = libvlc_meta_AlbumArtist,
105 106
    [vlc_meta_DiscNumber]   = libvlc_meta_DiscNumber,
    [vlc_meta_DiscTotal]    = libvlc_meta_DiscTotal
107 108
};

109 110
static libvlc_media_list_t *media_get_subitems( libvlc_media_t * p_md,
                                                bool b_create )
111 112 113 114
{
    libvlc_media_list_t *p_subitems = NULL;

    vlc_mutex_lock( &p_md->subitems_lock );
115
    if( p_md->p_subitems == NULL && b_create )
116 117 118
    {
        p_md->p_subitems = libvlc_media_list_new( p_md->p_libvlc_instance );
        if( p_md->p_subitems != NULL )
119 120
        {
            p_md->p_subitems->b_read_only = true;
121
            p_md->p_subitems->p_internal_md = p_md;
122
        }
123 124 125 126 127 128
    }
    p_subitems = p_md->p_subitems;
    vlc_mutex_unlock( &p_md->subitems_lock );
    return p_subitems;
}

129 130 131 132
/**************************************************************************
 * input_item_subitem_added (Private) (vlc event Callback)
 **************************************************************************/
static void input_item_subitem_added( const vlc_event_t *p_event,
133
                                       void * user_data )
134
{
135 136
    libvlc_media_t * p_md = user_data;
    libvlc_media_t * p_md_child;
137
    libvlc_media_list_t *p_subitems;
138 139
    libvlc_event_t event;

140
    p_md_child = libvlc_media_new_from_input_item(
141
                p_md->p_libvlc_instance,
142
                p_event->u.input_item_subitem_added.p_new_child );
143

144
    /* Add this to our media list */
145
    p_subitems = media_get_subitems( p_md, true );
146
    if( p_subitems != NULL )
147 148
    {
        libvlc_media_list_lock( p_subitems );
149
        libvlc_media_list_internal_add_media( p_subitems, p_md_child );
150 151
        libvlc_media_list_unlock( p_subitems );
    }
152

153
    /* Construct the event */
154
    event.type = libvlc_MediaSubItemAdded;
155
    event.u.media_subitem_added.new_child = p_md_child;
156 157 158

    /* Send the event */
    libvlc_event_send( p_md->p_event_manager, &event );
159
    libvlc_media_release( p_md_child );
160 161
}

162 163 164 165 166 167
/**************************************************************************
 * input_item_subitemtree_added (Private) (vlc event Callback)
 **************************************************************************/
static void input_item_subitemtree_added( const vlc_event_t * p_event,
                                          void * user_data )
{
168
    VLC_UNUSED( p_event );
169 170 171 172 173 174 175 176 177 178 179
    libvlc_media_t * p_md = user_data;
    libvlc_event_t event;

    /* Construct the event */
    event.type = libvlc_MediaSubItemTreeAdded;
    event.u.media_subitemtree_added.item = p_md;

    /* Send the event */
    libvlc_event_send( p_md->p_event_manager, &event );
}

180 181 182 183 184 185
/**************************************************************************
 * input_item_meta_changed (Private) (vlc event Callback)
 **************************************************************************/
static void input_item_meta_changed( const vlc_event_t *p_event,
                                     void * user_data )
{
186
    libvlc_media_t * p_md = user_data;
187 188 189
    libvlc_event_t event;

    /* Construct the event */
190
    event.type = libvlc_MediaMetaChanged;
191
    event.u.media_meta_changed.meta_type =
192 193 194 195 196 197
        vlc_to_libvlc_meta[p_event->u.input_item_meta_changed.meta_type];

    /* Send the event */
    libvlc_event_send( p_md->p_event_manager, &event );
}

198 199 200 201 202 203
/**************************************************************************
 * input_item_duration_changed (Private) (vlc event Callback)
 **************************************************************************/
static void input_item_duration_changed( const vlc_event_t *p_event,
                                         void * user_data )
{
204
    libvlc_media_t * p_md = user_data;
205 206 207
    libvlc_event_t event;

    /* Construct the event */
208
    event.type = libvlc_MediaDurationChanged;
209 210
    event.u.media_duration_changed.new_duration =
        from_mtime(p_event->u.input_item_duration_changed.new_duration);
211 212 213 214 215

    /* Send the event */
    libvlc_event_send( p_md->p_event_manager, &event );
}

216 217
static void send_parsed_changed( libvlc_media_t *p_md,
                                 libvlc_media_parsed_status_t new_status )
218 219 220
{
    libvlc_event_t event;

221 222
    vlc_mutex_lock( &p_md->parsed_lock );
    if( p_md->parsed_status == new_status )
223
    {
224
        vlc_mutex_unlock( &p_md->parsed_lock );
225 226
        return;
    }
227

228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
    /* Legacy: notify libvlc_media_parse */
    if( !p_md->is_parsed )
    {
        p_md->is_parsed = true;
        vlc_cond_broadcast( &p_md->parsed_cond );
    }

    p_md->parsed_status = new_status;
    if( p_md->parsed_status == libvlc_media_parsed_status_skipped )
        p_md->has_asked_preparse = false;

    vlc_mutex_unlock( &p_md->parsed_lock );

    if( new_status == libvlc_media_parsed_status_done )
    {
        libvlc_media_list_t *p_subitems = media_get_subitems( p_md, false );
        if( p_subitems != NULL )
        {
            /* notify the media list */
            libvlc_media_list_lock( p_subitems );
            libvlc_media_list_internal_end_reached( p_subitems );
            libvlc_media_list_unlock( p_subitems );
        }
    }
252

253
    /* Construct the event */
254
    event.type = libvlc_MediaParsedChanged;
255
    event.u.media_parsed_changed.new_status = new_status;
256 257

    /* Send the event */
258
    libvlc_event_send( p_md->p_event_manager, &event );
259 260
}

261 262 263 264 265 266 267
/**************************************************************************
 * input_item_preparse_ended (Private) (vlc event Callback)
 **************************************************************************/
static void input_item_preparse_ended( const vlc_event_t * p_event,
                                       void * user_data )
{
    libvlc_media_t * p_md = user_data;
268
    libvlc_media_parsed_status_t new_status;
269

270
    switch( p_event->u.input_item_preparse_ended.new_status )
271 272
    {
        case ITEM_PREPARSE_SKIPPED:
273
            new_status = libvlc_media_parsed_status_skipped;
274 275
            break;
        case ITEM_PREPARSE_FAILED:
276
            new_status = libvlc_media_parsed_status_failed;
277
            break;
278 279 280
        case ITEM_PREPARSE_TIMEOUT:
            new_status = libvlc_media_parsed_status_timeout;
            break;
281
        case ITEM_PREPARSE_DONE:
282
            new_status = libvlc_media_parsed_status_done;
283
            break;
284 285
        default:
            return;
286
    }
287
    send_parsed_changed( p_md, new_status );
288 289
}

290 291 292
/**************************************************************************
 * Install event handler (Private)
 **************************************************************************/
293
static void install_input_item_observer( libvlc_media_t *p_md )
294
{
295 296 297 298
    vlc_event_attach( &p_md->p_input_item->event_manager,
                      vlc_InputItemSubItemAdded,
                      input_item_subitem_added,
                      p_md );
299 300 301 302
    vlc_event_attach( &p_md->p_input_item->event_manager,
                      vlc_InputItemMetaChanged,
                      input_item_meta_changed,
                      p_md );
303 304 305 306
    vlc_event_attach( &p_md->p_input_item->event_manager,
                      vlc_InputItemDurationChanged,
                      input_item_duration_changed,
                      p_md );
307 308 309 310
    vlc_event_attach( &p_md->p_input_item->event_manager,
                      vlc_InputItemSubItemTreeAdded,
                      input_item_subitemtree_added,
                      p_md );
311 312 313 314
    vlc_event_attach( &p_md->p_input_item->event_manager,
                      vlc_InputItemPreparseEnded,
                      input_item_preparse_ended,
                      p_md );
315 316 317 318 319
}

/**************************************************************************
 * Uninstall event handler (Private)
 **************************************************************************/
320
static void uninstall_input_item_observer( libvlc_media_t *p_md )
321
{
322 323 324 325
    vlc_event_detach( &p_md->p_input_item->event_manager,
                      vlc_InputItemSubItemAdded,
                      input_item_subitem_added,
                      p_md );
326 327 328 329
    vlc_event_detach( &p_md->p_input_item->event_manager,
                      vlc_InputItemMetaChanged,
                      input_item_meta_changed,
                      p_md );
330 331 332 333
    vlc_event_detach( &p_md->p_input_item->event_manager,
                      vlc_InputItemDurationChanged,
                      input_item_duration_changed,
                      p_md );
334 335 336 337
    vlc_event_detach( &p_md->p_input_item->event_manager,
                      vlc_InputItemSubItemTreeAdded,
                      input_item_subitemtree_added,
                      p_md );
338 339 340 341
    vlc_event_detach( &p_md->p_input_item->event_manager,
                      vlc_InputItemPreparseEnded,
                      input_item_preparse_ended,
                      p_md );
342
}
343 344

/**************************************************************************
345 346 347
 * Create a new media descriptor object from an input_item
 * (libvlc internal)
 * That's the generic constructor
348
 **************************************************************************/
349
libvlc_media_t * libvlc_media_new_from_input_item(
350
                                   libvlc_instance_t *p_instance,
351
                                   input_item_t *p_input_item )
352
{
353
    libvlc_media_t * p_md;
354 355

    if (!p_input_item)
356
    {
357
        libvlc_printerr( "No input item given" );
358 359
        return NULL;
    }
360

Rémi Duraffort's avatar
Rémi Duraffort committed
361
    p_md = calloc( 1, sizeof(libvlc_media_t) );
362 363
    if( !p_md )
    {
364
        libvlc_printerr( "Not enough memory" );
365 366 367
        return NULL;
    }

368 369
    p_md->p_libvlc_instance = p_instance;
    p_md->p_input_item      = p_input_item;
370
    p_md->i_refcount        = 1;
371

372 373
    vlc_cond_init(&p_md->parsed_cond);
    vlc_mutex_init(&p_md->parsed_lock);
374
    vlc_mutex_init(&p_md->subitems_lock);
375

376
    p_md->state = libvlc_NothingSpecial;
377

378 379 380 381
    /* A media descriptor can be a playlist. When you open a playlist
     * It can give a bunch of item to read. */
    p_md->p_subitems        = NULL;

382
    p_md->p_event_manager = libvlc_event_manager_new( p_md );
383 384 385 386 387
    if( unlikely(p_md->p_event_manager == NULL) )
    {
        free(p_md);
        return NULL;
    }
388

389
    vlc_gc_incref( p_md->p_input_item );
390

391 392
    install_input_item_observer( p_md );

393
    libvlc_retain( p_instance );
394
    return p_md;
395 396
}

397
/**************************************************************************
398
 * Create a new media descriptor object
399
 **************************************************************************/
400 401
libvlc_media_t *libvlc_media_new_location( libvlc_instance_t *p_instance,
                                           const char * psz_mrl )
402
{
403
    input_item_t * p_input_item;
404
    libvlc_media_t * p_md;
405

406
    p_input_item = input_item_New( psz_mrl, NULL );
407

408
    if (!p_input_item)
409
    {
410
        libvlc_printerr( "Not enough memory" );
411 412
        return NULL;
    }
413

414
    p_md = libvlc_media_new_from_input_item( p_instance, p_input_item );
415

416
    /* The p_input_item is retained in libvlc_media_new_from_input_item */
417
    vlc_gc_decref( p_input_item );
418

419
    return p_md;
420 421
}

422 423 424
libvlc_media_t *libvlc_media_new_path( libvlc_instance_t *p_instance,
                                       const char *path )
{
425
    char *mrl = vlc_path2uri( path, NULL );
426 427
    if( unlikely(mrl == NULL) )
    {
428
        libvlc_printerr( "%s", vlc_strerror_c(errno) );
429 430 431 432 433 434 435 436
        return NULL;
    }

    libvlc_media_t *m = libvlc_media_new_location( p_instance, mrl );
    free( mrl );
    return m;
}

437 438 439 440 441 442 443 444
libvlc_media_t *libvlc_media_new_fd( libvlc_instance_t *p_instance, int fd )
{
    char mrl[16];
    snprintf( mrl, sizeof(mrl), "fd://%d", fd );

    return libvlc_media_new_location( p_instance, mrl );
}

445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464
libvlc_media_t *libvlc_media_new_callbacks(libvlc_instance_t *p_instance,
                                           libvlc_media_open_cb open_cb,
                                           libvlc_media_read_cb read_cb,
                                           libvlc_media_seek_cb seek_cb,
                                           libvlc_media_close_cb close_cb,
                                           void *opaque)
{
    libvlc_media_t *m = libvlc_media_new_location(p_instance, "imem://");
    if (unlikely(m == NULL))
        return NULL;

    assert(read_cb != NULL);
    input_item_AddOpaque(m->p_input_item, "imem-data", opaque);
    input_item_AddOpaque(m->p_input_item, "imem-open", open_cb);
    input_item_AddOpaque(m->p_input_item, "imem-read", read_cb);
    input_item_AddOpaque(m->p_input_item, "imem-seek", seek_cb);
    input_item_AddOpaque(m->p_input_item, "imem-close", close_cb);
    return m;
}

465 466 467
/**************************************************************************
 * Create a new media descriptor object
 **************************************************************************/
468 469
libvlc_media_t * libvlc_media_new_as_node( libvlc_instance_t *p_instance,
                                           const char * psz_name )
470 471
{
    input_item_t * p_input_item;
472
    libvlc_media_t * p_md;
473
    libvlc_media_list_t * p_subitems;
474

475
    p_input_item = input_item_New( "vlc://nop", psz_name );
476 477 478

    if (!p_input_item)
    {
479
        libvlc_printerr( "Not enough memory" );
480 481 482
        return NULL;
    }

483
    p_md = libvlc_media_new_from_input_item( p_instance, p_input_item );
484
    input_item_Release( p_input_item );
485

486
    p_subitems = media_get_subitems( p_md, true );
487 488 489 490
    if( p_subitems == NULL) {
        libvlc_media_release( p_md );
        return NULL;
    }
491 492 493 494

    return p_md;
}

495 496
/**************************************************************************
 * Add an option to the media descriptor,
497 498
 * that will be used to determine how the media_player will read the
 * media. This allow to use VLC advanced reading/streaming
499 500 501 502
 * options in a per-media basis
 *
 * The options are detailled in vlc --long-help, for instance "--sout-all"
 **************************************************************************/
503 504
void libvlc_media_add_option( libvlc_media_t * p_md,
                              const char * psz_option )
505
{
506
    libvlc_media_add_option_flag( p_md, psz_option,
507
                          VLC_INPUT_OPTION_UNIQUE|VLC_INPUT_OPTION_TRUSTED );
508 509
}

510
/**************************************************************************
511
 * Same as libvlc_media_add_option but with configurable flags.
512
 **************************************************************************/
513
void libvlc_media_add_option_flag( libvlc_media_t * p_md,
514
                                   const char * ppsz_option,
515
                                   unsigned i_flags )
516
{
517
    input_item_AddOption( p_md->p_input_item, ppsz_option, i_flags );
518 519
}

520 521 522
/**************************************************************************
 * Delete a media descriptor object
 **************************************************************************/
523
void libvlc_media_release( libvlc_media_t *p_md )
524
{
525
    if (!p_md)
526 527
        return;

528 529 530 531
    p_md->i_refcount--;

    if( p_md->i_refcount > 0 )
        return;
532

533 534
    uninstall_input_item_observer( p_md );

535
    /* Cancel asynchronous parsing (if any) */
536
    libvlc_MetadataCancel( p_md->p_libvlc_instance->p_libvlc_int, p_md );
537

538 539
    if( p_md->p_subitems )
        libvlc_media_list_release( p_md->p_subitems );
540

541 542
    vlc_gc_decref( p_md->p_input_item );

543 544
    vlc_cond_destroy( &p_md->parsed_cond );
    vlc_mutex_destroy( &p_md->parsed_lock );
545
    vlc_mutex_destroy( &p_md->subitems_lock );
546

547 548
    /* Construct the event */
    libvlc_event_t event;
549
    event.type = libvlc_MediaFreed;
550
    event.u.media_freed.md = p_md;
551 552 553 554

    /* Send the event */
    libvlc_event_send( p_md->p_event_manager, &event );

555
    libvlc_event_manager_release( p_md->p_event_manager );
556
    libvlc_release( p_md->p_libvlc_instance );
557 558 559 560
    free( p_md );
}

/**************************************************************************
561 562
 * Retain a media descriptor object
 **************************************************************************/
563
void libvlc_media_retain( libvlc_media_t *p_md )
564
{
565
    assert (p_md);
566 567 568 569 570
    p_md->i_refcount++;
}

/**************************************************************************
 * Duplicate a media descriptor object
571
 **************************************************************************/
572 573
libvlc_media_t *
libvlc_media_duplicate( libvlc_media_t *p_md_orig )
574
{
575
    return libvlc_media_new_from_input_item(
576
        p_md_orig->p_libvlc_instance, p_md_orig->p_input_item );
577 578
}

579
/**************************************************************************
580
 * Get mrl from a media descriptor object
581 582
 **************************************************************************/
char *
583
libvlc_media_get_mrl( libvlc_media_t * p_md )
584
{
585
    assert( p_md );
586
    return input_item_GetURI( p_md->p_input_item );
587 588
}

589
/**************************************************************************
590
 * Getter for meta information
591 592
 **************************************************************************/

593
char *libvlc_media_get_meta( libvlc_media_t *p_md, libvlc_meta_t e_meta )
594
{
595
    char *psz_meta = NULL;
596

597 598 599 600 601 602 603 604 605 606 607 608 609
    if( e_meta == libvlc_meta_NowPlaying )
    {
        psz_meta = input_item_GetNowPlayingFb( p_md->p_input_item );
    }
    else
    {
        psz_meta = input_item_GetMeta( p_md->p_input_item,
                                             libvlc_to_vlc_meta[e_meta] );
        /* Should be integrated in core */
        if( psz_meta == NULL && e_meta == libvlc_meta_Title
         && p_md->p_input_item->psz_name != NULL )
            psz_meta = strdup( p_md->p_input_item->psz_name );
    }
610
    return psz_meta;
611
}
612

Rémi Duraffort's avatar
Rémi Duraffort committed
613 614 615 616 617 618 619 620 621 622 623 624 625
/**************************************************************************
 * Setter for meta information
 **************************************************************************/

void libvlc_media_set_meta( libvlc_media_t *p_md, libvlc_meta_t e_meta, const char *psz_value )
{
    assert( p_md );
    input_item_SetMeta( p_md->p_input_item, libvlc_to_vlc_meta[e_meta], psz_value );
}

int libvlc_media_save_meta( libvlc_media_t *p_md )
{
    assert( p_md );
626
    vlc_object_t *p_obj = VLC_OBJECT(p_md->p_libvlc_instance->p_libvlc_int);
Rémi Duraffort's avatar
Rémi Duraffort committed
627 628 629
    return input_item_WriteMeta( p_obj, p_md->p_input_item ) == VLC_SUCCESS;
}

630 631 632 633 634
/**************************************************************************
 * Getter for state information
 * Can be error, playing, buffering, NothingSpecial.
 **************************************************************************/

635
libvlc_state_t
636
libvlc_media_get_state( libvlc_media_t *p_md )
637
{
638
    assert( p_md );
639 640 641 642 643 644 645 646
    return p_md->state;
}

/**************************************************************************
 * Setter for state information (LibVLC Internal)
 **************************************************************************/

void
647
libvlc_media_set_state( libvlc_media_t *p_md,
648
                                   libvlc_state_t state )
649 650 651 652 653 654
{
    libvlc_event_t event;

    p_md->state = state;

    /* Construct the event */
655
    event.type = libvlc_MediaStateChanged;
656
    event.u.media_state_changed.new_state = state;
657 658 659 660 661

    /* Send the event */
    libvlc_event_send( p_md->p_event_manager, &event );
}

662 663 664 665
/**************************************************************************
 * subitems
 **************************************************************************/
libvlc_media_list_t *
666
libvlc_media_subitems( libvlc_media_t * p_md )
667
{
668
    libvlc_media_list_t *p_subitems = media_get_subitems( p_md, true );
669 670 671
    if( p_subitems )
        libvlc_media_list_retain( p_subitems );
    return p_subitems;
672
}
673

674
/**************************************************************************
Rémi Duraffort's avatar
Rémi Duraffort committed
675
 * Getter for statistics information
676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708
 **************************************************************************/
int libvlc_media_get_stats( libvlc_media_t *p_md,
                            libvlc_media_stats_t *p_stats )
{
    if( !p_md->p_input_item )
        return false;

    input_stats_t *p_itm_stats = p_md->p_input_item->p_stats;
    vlc_mutex_lock( &p_itm_stats->lock );
    p_stats->i_read_bytes = p_itm_stats->i_read_bytes;
    p_stats->f_input_bitrate = p_itm_stats->f_input_bitrate;

    p_stats->i_demux_read_bytes = p_itm_stats->i_demux_read_bytes;
    p_stats->f_demux_bitrate = p_itm_stats->f_demux_bitrate;
    p_stats->i_demux_corrupted = p_itm_stats->i_demux_corrupted;
    p_stats->i_demux_discontinuity = p_itm_stats->i_demux_discontinuity;

    p_stats->i_decoded_video = p_itm_stats->i_decoded_video;
    p_stats->i_decoded_audio = p_itm_stats->i_decoded_audio;

    p_stats->i_displayed_pictures = p_itm_stats->i_displayed_pictures;
    p_stats->i_lost_pictures = p_itm_stats->i_lost_pictures;

    p_stats->i_played_abuffers = p_itm_stats->i_played_abuffers;
    p_stats->i_lost_abuffers = p_itm_stats->i_lost_abuffers;

    p_stats->i_sent_packets = p_itm_stats->i_sent_packets;
    p_stats->i_sent_bytes = p_itm_stats->i_sent_bytes;
    p_stats->f_send_bitrate = p_itm_stats->f_send_bitrate;
    vlc_mutex_unlock( &p_itm_stats->lock );
    return true;
}

709 710 711 712
/**************************************************************************
 * event_manager
 **************************************************************************/
libvlc_event_manager_t *
713
libvlc_media_event_manager( libvlc_media_t * p_md )
714
{
715
    assert( p_md );
716

717 718
    return p_md->p_event_manager;
}
719 720

/**************************************************************************
721
 * Get duration of media object (in ms)
722
 **************************************************************************/
723
int64_t
724
libvlc_media_get_duration( libvlc_media_t * p_md )
725
{
726
    assert( p_md );
727

728
    if( !p_md->p_input_item )
729
    {
730
        libvlc_printerr( "No input item" );
731 732
        return -1;
    }
733

734 735 736
    if (!input_item_IsPreparsed( p_md->p_input_item ))
        return -1;

737
    return from_mtime(input_item_GetDuration( p_md->p_input_item ));
738 739
}

740
static int media_parse(libvlc_media_t *media, bool b_async,
741
                       libvlc_media_parse_flag_t parse_flag, int timeout)
742
{
743 744 745 746 747
    bool needed;

    vlc_mutex_lock(&media->parsed_lock);
    needed = !media->has_asked_preparse;
    media->has_asked_preparse = true;
748 749
    if (needed)
        media->is_parsed = false;
750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768
    vlc_mutex_unlock(&media->parsed_lock);

    if (needed)
    {
        libvlc_int_t *libvlc = media->p_libvlc_instance->p_libvlc_int;
        input_item_t *item = media->p_input_item;
        input_item_meta_request_option_t art_scope = META_REQUEST_OPTION_NONE;
        input_item_meta_request_option_t parse_scope = META_REQUEST_OPTION_SCOPE_LOCAL;
        int ret;

        if (parse_flag & libvlc_media_fetch_local)
            art_scope |= META_REQUEST_OPTION_SCOPE_LOCAL;
        if (parse_flag & libvlc_media_fetch_network)
            art_scope |= META_REQUEST_OPTION_SCOPE_NETWORK;
        if (art_scope != META_REQUEST_OPTION_NONE) {
            ret = libvlc_ArtRequest(libvlc, item, art_scope);
            if (ret != VLC_SUCCESS)
                return ret;
        }
769

770 771
        if (parse_flag & libvlc_media_parse_network)
            parse_scope |= META_REQUEST_OPTION_SCOPE_NETWORK;
772 773
        if (parse_flag & libvlc_media_do_interact)
            parse_scope |= META_REQUEST_OPTION_DO_INTERACT;
774
        ret = libvlc_MetadataRequest(libvlc, item, parse_scope, timeout, media);
775 776 777 778 779 780 781 782 783 784 785 786 787 788
        if (ret != VLC_SUCCESS)
            return ret;
    }
    else
        return VLC_EGENERIC;

    if (!b_async)
    {
        vlc_mutex_lock(&media->parsed_lock);
        while (!media->is_parsed)
            vlc_cond_wait(&media->parsed_cond, &media->parsed_lock);
        vlc_mutex_unlock(&media->parsed_lock);
    }
    return VLC_SUCCESS;
789 790
}

791
/**************************************************************************
792
 * Parse the media and wait.
793 794 795 796
 **************************************************************************/
void
libvlc_media_parse(libvlc_media_t *media)
{
797
    media_parse( media, false, libvlc_media_fetch_local, -1 );
798 799 800
}

/**************************************************************************
801
 * Parse the media but do not wait.
802 803 804 805
 **************************************************************************/
void
libvlc_media_parse_async(libvlc_media_t *media)
{
806
    media_parse( media, true, libvlc_media_fetch_local, -1 );
807
}
808

809 810 811 812 813
/**************************************************************************
 * Parse the media asynchronously with options.
 **************************************************************************/
int
libvlc_media_parse_with_options( libvlc_media_t *media,
814 815
                                 libvlc_media_parse_flag_t parse_flag,
                                 int timeout )
816
{
817
    return media_parse( media, true, parse_flag, timeout ) == VLC_SUCCESS ? 0 : -1;
818 819
}

820 821 822 823 824 825
void
libvlc_media_parse_stop( libvlc_media_t *media )
{
    libvlc_MetadataCancel( media->p_libvlc_instance->p_libvlc_int, media );
}

826
/**************************************************************************
827
 * Get parsed status for media object.
828
 **************************************************************************/
829
int
830
libvlc_media_is_parsed(libvlc_media_t *media)
831
{
832
    bool parsed;
833

834 835 836 837
    vlc_mutex_lock(&media->parsed_lock);
    parsed = media->is_parsed;
    vlc_mutex_unlock(&media->parsed_lock);
    return parsed;
838 839
}

840 841 842 843 844 845 846 847 848 849 850
libvlc_media_parsed_status_t
libvlc_media_get_parsed_status(libvlc_media_t *media)
{
    libvlc_media_parsed_status_t status;

    vlc_mutex_lock(&media->parsed_lock);
    status = media->parsed_status;
    vlc_mutex_unlock(&media->parsed_lock);
    return status;
}

851
/**************************************************************************
Pierre's avatar
Pierre committed
852 853
 * Sets media descriptor's user_data. user_data is specialized data
 * accessed by the host application, VLC.framework uses it as a pointer to
854
 * an native object that references a libvlc_media_t pointer
855
 **************************************************************************/
Pierre's avatar
Pierre committed
856
void
857
libvlc_media_set_user_data( libvlc_media_t * p_md, void * p_new_user_data )
858
{
859 860
    assert( p_md );
    p_md->p_user_data = p_new_user_data;
861 862 863
}

/**************************************************************************
Pierre's avatar
Pierre committed
864 865
 * Get media descriptor's user_data. user_data is specialized data
 * accessed by the host application, VLC.framework uses it as a pointer to
866
 * an native object that references a libvlc_media_t pointer
867 868
 **************************************************************************/
void *
869
libvlc_media_get_user_data( libvlc_media_t * p_md )
870
{
871 872
    assert( p_md );
    return p_md->p_user_data;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
873
}
874 875 876 877 878

/**************************************************************************
 * Get media descriptor's elementary streams description
 **************************************************************************/
int
879
libvlc_media_get_tracks_info( libvlc_media_t *p_md, libvlc_media_track_info_t ** pp_es )
880 881 882 883 884 885 886
{
    assert( p_md );

    input_item_t *p_input_item = p_md->p_input_item;
    vlc_mutex_lock( &p_input_item->lock );

    const int i_es = p_input_item->i_es;
887
    *pp_es = (i_es > 0) ? malloc( i_es * sizeof(libvlc_media_track_info_t) ) : NULL;
888

889
    if( !*pp_es ) /* no ES, or OOM */
890 891 892 893 894 895 896 897
    {
        vlc_mutex_unlock( &p_input_item->lock );
        return 0;
    }

    /* Fill array */
    for( int i = 0; i < i_es; i++ )
    {
898
        libvlc_media_track_info_t *p_mes = *pp_es+i;
899 900 901
        const es_format_t *p_es = p_input_item->es[i];

        p_mes->i_codec = p_es->i_codec;
902
        p_mes->i_id = p_es->i_id;
903 904 905 906 907 908 909 910

        p_mes->i_profile = p_es->i_profile;
        p_mes->i_level = p_es->i_level;

        switch(p_es->i_cat)
        {
        case UNKNOWN_ES:
        default:
911
            p_mes->i_type = libvlc_track_unknown;
912 913
            break;
        case VIDEO_ES:
914
            p_mes->i_type = libvlc_track_video;
915 916
            p_mes->u.video.i_height = p_es->video.i_visible_height;
            p_mes->u.video.i_width = p_es->video.i_visible_width;
917 918
            break;
        case AUDIO_ES:
919
            p_mes->i_type = libvlc_track_audio;
920 921
            p_mes->u.audio.i_channels = p_es->audio.i_channels;
            p_mes->u.audio.i_rate = p_es->audio.i_rate;
922 923
            break;
        case SPU_ES:
924
            p_mes->i_type = libvlc_track_text;
925 926 927 928 929 930 931
            break;
        }
    }

    vlc_mutex_unlock( &p_input_item->lock );
    return i_es;
}
932

933
unsigned
934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963
libvlc_media_tracks_get( libvlc_media_t *p_md, libvlc_media_track_t *** pp_es )
{
    assert( p_md );

    input_item_t *p_input_item = p_md->p_input_item;
    vlc_mutex_lock( &p_input_item->lock );

    const int i_es = p_input_item->i_es;
    *pp_es = (i_es > 0) ? calloc( i_es, sizeof(**pp_es) ) : NULL;

    if( !*pp_es ) /* no ES, or OOM */
    {
        vlc_mutex_unlock( &p_input_item->lock );
        return 0;
    }

    /* Fill array */
    for( int i = 0; i < i_es; i++ )
    {
        libvlc_media_track_t *p_mes = calloc( 1, sizeof(*p_mes) );
        if ( p_mes )
        {
            p_mes->audio = malloc( __MAX(__MAX(sizeof(*p_mes->audio),
                                               sizeof(*p_mes->video)),
                                               sizeof(*p_mes->subtitle)) );
        }
        if ( !p_mes || !p_mes->audio )
        {
            libvlc_media_tracks_release( *pp_es, i_es );
            *pp_es = NULL;
964
            free( p_mes );
965
            vlc_mutex_unlock( &p_input_item->lock );
966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990
            return 0;
        }
        (*pp_es)[i] = p_mes;

        const es_format_t *p_es = p_input_item->es[i];

        p_mes->i_codec = p_es->i_codec;
        p_mes->i_original_fourcc = p_es->i_original_fourcc;
        p_mes->i_id = p_es->i_id;

        p_mes->i_profile = p_es->i_profile;
        p_mes->i_level = p_es->i_level;

        p_mes->i_bitrate = p_es->i_bitrate;
        p_mes->psz_language = p_es->psz_language != NULL ? strdup(p_es->psz_language) : NULL;
        p_mes->psz_description = p_es->psz_description != NULL ? strdup(p_es->psz_description) : NULL;

        switch(p_es->i_cat)
        {
        case UNKNOWN_ES:
        default:
            p_mes->i_type = libvlc_track_unknown;
            break;
        case VIDEO_ES:
            p_mes->i_type = libvlc_track_video;
991 992
            p_mes->video->i_height = p_es->video.i_visible_height;
            p_mes->video->i_width = p_es->video.i_visible_width;
993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014
            p_mes->video->i_sar_num = p_es->video.i_sar_num;
            p_mes->video->i_sar_den = p_es->video.i_sar_den;
            p_mes->video->i_frame_rate_num = p_es->video.i_frame_rate;
            p_mes->video->i_frame_rate_den = p_es->video.i_frame_rate_base;
            break;
        case AUDIO_ES:
            p_mes->i_type = libvlc_track_audio;
            p_mes->audio->i_channels = p_es->audio.i_channels;
            p_mes->audio->i_rate = p_es->audio.i_rate;
            break;
        case SPU_ES:
            p_mes->i_type = libvlc_track_text;
            p_mes->subtitle->psz_encoding = p_es->subs.psz_encoding != NULL ?
                                            strdup(p_es->subs.psz_encoding) : NULL;
            break;
        }
    }

    vlc_mutex_unlock( &p_input_item->lock );
    return i_es;
}

1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034
/**************************************************************************
 * Get codec description from media elementary stream
 **************************************************************************/
const char *
libvlc_media_get_codec_description( libvlc_track_type_t i_type,
                                    uint32_t i_codec )
{
    switch( i_type )
    {
        case libvlc_track_audio:
            return vlc_fourcc_GetDescription( AUDIO_ES, i_codec );
        case libvlc_track_video:
            return vlc_fourcc_GetDescription( VIDEO_ES, i_codec );
        case libvlc_track_text:
            return vlc_fourcc_GetDescription( SPU_ES, i_codec );
        case libvlc_track_unknown:
        default:
            return vlc_fourcc_GetDescription( UNKNOWN_ES, i_codec );
    }
}
1035 1036 1037 1038

/**************************************************************************
 * Release media descriptor's elementary streams description array
 **************************************************************************/
1039
void libvlc_media_tracks_release( libvlc_media_track_t **p_tracks, unsigned i_count )
1040
{
1041
    for( unsigned i = 0; i < i_count; ++i )
1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064
    {
        if ( !p_tracks[i] )
            continue;
        free( p_tracks[i]->psz_language );
        free( p_tracks[i]->psz_description );
        switch( p_tracks[i]->i_type )
        {
        case libvlc_track_audio:
            break;
        case libvlc_track_video:
            break;
        case libvlc_track_text:
            free( p_tracks[i]->subtitle->psz_encoding );
            break;
        case libvlc_track_unknown:
        default:
            break;
        }
        free( p_tracks[i]->audio );
        free( p_tracks[i] );
    }
    free( p_tracks );
}
1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096

/**************************************************************************
 * Get the media type of the media descriptor object
 **************************************************************************/
libvlc_media_type_t libvlc_media_get_type( libvlc_media_t *p_md )
{
    assert( p_md );

    int i_type;
    input_item_t *p_input_item = p_md->p_input_item;

    vlc_mutex_lock( &p_input_item->lock );
    i_type = p_md->p_input_item->i_type;
    vlc_mutex_unlock( &p_input_item->lock );

    switch( i_type )
    {
    case ITEM_TYPE_FILE:
        return libvlc_media_type_file;
    case ITEM_TYPE_NODE:
    case ITEM_TYPE_DIRECTORY:
        return libvlc_media_type_directory;
    case ITEM_TYPE_DISC:
        return libvlc_media_type_disc;
    case ITEM_TYPE_STREAM:
        return libvlc_media_type_stream;
    case ITEM_TYPE_PLAYLIST:
        return libvlc_media_type_playlist;
    default:
        return libvlc_media_type_unknown;
    }
}
1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113

int libvlc_media_slaves_add( libvlc_media_t *p_md,
                             libvlc_media_slave_type_t i_type,
                             unsigned int i_priority,
                             const char *psz_uri )
{
    assert( p_md && psz_uri );
    input_item_t *p_input_item = p_md->p_input_item;

    enum slave_type i_input_slave_type;
    switch( i_type )
    {
    case libvlc_media_slave_type_subtitle:
        i_input_slave_type = SLAVE_TYPE_SPU;
        break;
    case libvlc_media_slave_type_audio:
        i_input_slave_type = SLAVE_TYPE_AUDIO;
1114
        break;
1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179
    default:
        vlc_assert_unreachable();
        return -1;
    }

    enum slave_priority i_input_slave_priority;
    switch( i_priority )
    {
    case 0:
        i_input_slave_priority = SLAVE_PRIORITY_MATCH_NONE;
        break;
    case 1:
        i_input_slave_priority = SLAVE_PRIORITY_MATCH_RIGHT;
        break;
    case 2:
        i_input_slave_priority = SLAVE_PRIORITY_MATCH_LEFT;
        break;
    case 3:
        i_input_slave_priority = SLAVE_PRIORITY_MATCH_ALL;
        break;
    default:
    case 4:
        i_input_slave_priority = SLAVE_PRIORITY_USER;
        break;
    }

    input_item_slave_t *p_slave = input_item_slave_New( psz_uri,
                                                      i_input_slave_type,
                                                      i_input_slave_priority );
    if( p_slave == NULL )
        return -1;
    return input_item_AddSlave( p_input_item, p_slave ) == VLC_SUCCESS ? 0 : -1;
}

void libvlc_media_slaves_clear( libvlc_media_t *p_md )
{
    assert( p_md );
    input_item_t *p_input_item = p_md->p_input_item;

    vlc_mutex_lock( &p_input_item->lock );
    for( int i = 0; i < p_input_item->i_slaves; i++ )
        input_item_slave_Delete( p_input_item->pp_slaves[i] );
    TAB_CLEAN( p_input_item->i_slaves, p_input_item->pp_slaves );
    vlc_mutex_unlock( &p_input_item->lock );
}

unsigned int libvlc_media_slaves_get( libvlc_media_t *p_md,
                                      libvlc_media_slave_t ***ppp_slaves )
{
    assert( p_md && ppp_slaves );
    input_item_t *p_input_item = p_md->p_input_item;

    vlc_mutex_lock( &p_input_item->lock );

    int i_count = p_input_item->i_slaves;
    if( i_count <= 0 )
        return vlc_mutex_unlock( &p_input_item->lock ), 0;

    libvlc_media_slave_t **pp_slaves = calloc( i_count, sizeof(*pp_slaves) );
    if( pp_slaves == NULL )
        return vlc_mutex_unlock( &p_input_item->lock ), 0;

    for( int i = 0; i < i_count; ++i )
    {
        input_item_slave_t *p_item_slave = p_input_item->pp_slaves[i];
1180
        assert( p_item_slave->i_priority >= SLAVE_PRIORITY_MATCH_NONE );
1181

1182
        /* also allocate psz_uri buffer at the end of the struct */