media.c 41.4 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 536 537 538
    /* Cancel asynchronous parsing (if any) */
    if( p_md->p_input_item )
        libvlc_MetadataCancel( p_md->p_libvlc_instance->p_libvlc_int, p_md );

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

542 543
    vlc_gc_decref( p_md->p_input_item );

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

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

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

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

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

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

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

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

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

598 599 600 601 602 603 604 605 606 607 608 609 610
    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 );
    }
611
    return psz_meta;
612
}
613

Rémi Duraffort's avatar
Rémi Duraffort committed
614 615 616 617 618 619 620 621 622 623 624 625 626
/**************************************************************************
 * 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 );
627
    vlc_object_t *p_obj = VLC_OBJECT(p_md->p_libvlc_instance->p_libvlc_int);
Rémi Duraffort's avatar
Rémi Duraffort committed
628 629 630
    return input_item_WriteMeta( p_obj, p_md->p_input_item ) == VLC_SUCCESS;
}

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

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

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

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

    p_md->state = state;

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

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

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

675
/**************************************************************************
Rémi Duraffort's avatar
Rémi Duraffort committed
676
 * Getter for statistics information
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 709
 **************************************************************************/
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;
}

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

718 719
    return p_md->p_event_manager;
}
720 721

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

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

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

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

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

    vlc_mutex_lock(&media->parsed_lock);
    needed = !media->has_asked_preparse;
    media->has_asked_preparse = true;
749 750
    if (needed)
        media->is_parsed = false;
751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769
    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;
        }
770

771 772
        if (parse_flag & libvlc_media_parse_network)
            parse_scope |= META_REQUEST_OPTION_SCOPE_NETWORK;
773 774
        if (parse_flag & libvlc_media_do_interact)
            parse_scope |= META_REQUEST_OPTION_DO_INTERACT;
775
        ret = libvlc_MetadataRequest(libvlc, item, parse_scope, timeout, media);
776 777 778 779 780 781 782 783 784 785 786 787 788 789
        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;
790 791
}

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

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

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

821
/**************************************************************************
822
 * Get parsed status for media object.
823
 **************************************************************************/
824
int
825
libvlc_media_is_parsed(libvlc_media_t *media)
826
{
827
    bool parsed;
828

829 830 831 832
    vlc_mutex_lock(&media->parsed_lock);
    parsed = media->is_parsed;
    vlc_mutex_unlock(&media->parsed_lock);
    return parsed;
833 834
}

835 836 837 838 839 840 841 842 843 844 845
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;
}

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

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

/**************************************************************************
 * Get media descriptor's elementary streams description
 **************************************************************************/
int
874
libvlc_media_get_tracks_info( libvlc_media_t *p_md, libvlc_media_track_info_t ** pp_es )
875 876 877 878 879 880 881
{
    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;
882
    *pp_es = (i_es > 0) ? malloc( i_es * sizeof(libvlc_media_track_info_t) ) : NULL;
883

884
    if( !*pp_es ) /* no ES, or OOM */
885 886 887 888 889 890 891 892
    {
        vlc_mutex_unlock( &p_input_item->lock );
        return 0;
    }

    /* Fill array */
    for( int i = 0; i < i_es; i++ )
    {
893
        libvlc_media_track_info_t *p_mes = *pp_es+i;
894 895 896
        const es_format_t *p_es = p_input_item->es[i];

        p_mes->i_codec = p_es->i_codec;
897
        p_mes->i_id = p_es->i_id;
898 899 900 901 902 903 904 905

        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:
906
            p_mes->i_type = libvlc_track_unknown;
907 908
            break;
        case VIDEO_ES:
909
            p_mes->i_type = libvlc_track_video;
910 911
            p_mes->u.video.i_height = p_es->video.i_visible_height;
            p_mes->u.video.i_width = p_es->video.i_visible_width;
912 913
            break;
        case AUDIO_ES:
914
            p_mes->i_type = libvlc_track_audio;
915 916
            p_mes->u.audio.i_channels = p_es->audio.i_channels;
            p_mes->u.audio.i_rate = p_es->audio.i_rate;
917 918
            break;
        case SPU_ES:
919
            p_mes->i_type = libvlc_track_text;
920 921 922 923 924 925 926
            break;
        }
    }

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

928
unsigned
929 930 931 932 933 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
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;
959
            free( p_mes );
960
            vlc_mutex_unlock( &p_input_item->lock );
961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985
            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;
986 987
            p_mes->video->i_height = p_es->video.i_visible_height;
            p_mes->video->i_width = p_es->video.i_visible_width;
988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009
            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;
}

1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029
/**************************************************************************
 * 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 );
    }
}
1030 1031 1032 1033

/**************************************************************************
 * Release media descriptor's elementary streams description array
 **************************************************************************/
1034
void libvlc_media_tracks_release( libvlc_media_track_t **p_tracks, unsigned i_count )
1035
{
1036
    for( unsigned i = 0; i < i_count; ++i )
1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059
    {
        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 );
}
1060 1061 1062 1063 1064 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

/**************************************************************************
 * 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;
    }
}
1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108

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;
1109
        break;
1110 1111 1112 1113 1114 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
    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];
1175
        assert( p_item_slave->i_priority >= SLAVE_PRIORITY_MATCH_NONE );
1176

1177
        /* also allocate psz_uri buffer at the end of the struct */
1178 1179 1180 1181 1182 1183 1184 1185
        libvlc_media_slave_t *p_slave = malloc( sizeof(*p_slave) +
                                                strlen( p_item_slave->psz_uri )
                                                + 1 );
        if( p_slave == NULL )
        {
            libvlc_media_slaves_release(pp_slaves, i);
            return vlc_mutex_unlock( &p_input_item->lock ), 0;
        }
1186
        p_slave->psz_uri = (char *) ((uint8_t *)p_slave) + sizeof(*p_slave);
1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231
        strcpy( p_slave->psz_uri, p_item_slave->psz_uri );

        switch( p_item_slave->i_type )
        {
        case SLAVE_TYPE_SPU:
            p_slave->i_type = libvlc_media_slave_type_subtitle;
            break;
        case SLAVE_TYPE_AUDIO:
            p_slave->i_type = libvlc_media_slave_type_audio;
            break;
        default:
            vlc_assert_unreachable();
        }

        switch( p_item_slave->i_priority )
        {
        case SLAVE_PRIORITY_MATCH_NONE:
            p_slave->i_priority = 0;
            break;
        case SLAVE_PRIORITY_MATCH_RIGHT:
            p_slave->i_priority = 1;
            break;
        case SLAVE_PRIORITY_MATCH_LEFT:
            p_slave->i_priority = 2;
            break;
        case SLAVE_PRIORITY_MATCH_ALL:
            p_slave->i_priority = 3;
            break;
        case SLAVE_PRIORITY_USER:
            p_slave->i_priority = 4;
            break;