media_player.c 39.6 KB
Newer Older
1
/*****************************************************************************
2
 * media_player.c: Libvlc API Media Instance management functions
3
 *****************************************************************************
4
 * Copyright (C) 2005-2009 the VideoLAN team
5 6
 * $Id$
 *
7
 * Authors: Clément Stenac <zorglub@videolan.org>
8 9 10 11 12 13 14 15 16 17 18 19 20
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
Antoine Cellerier's avatar
Antoine Cellerier committed
21
 * 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

30
#include <vlc/libvlc.h>
31 32 33
#include <vlc/libvlc_media.h>
#include <vlc/libvlc_events.h>

34 35
#include <vlc_demux.h>
#include <vlc_input.h>
36
#include <vlc_vout.h>
37

38
#include "libvlc.h"
39 40 41 42

#include "libvlc_internal.h"
#include "media_internal.h" // libvlc_media_set_state()
#include "media_player_internal.h"
43

44
static int
45 46 47 48 49 50 51 52
input_seekable_changed( vlc_object_t * p_this, char const * psz_cmd,
                        vlc_value_t oldval, vlc_value_t newval,
                        void * p_userdata );
static int
input_pausable_changed( vlc_object_t * p_this, char const * psz_cmd,
                        vlc_value_t oldval, vlc_value_t newval,
                        void * p_userdata );
static int
Laurent Aimar's avatar
Laurent Aimar committed
53
input_event_changed( vlc_object_t * p_this, char const * psz_cmd,
54 55 56
                     vlc_value_t oldval, vlc_value_t newval,
                     void * p_userdata );

57 58 59
static int SnapshotTakenCallback( vlc_object_t *p_this, char const *psz_cmd,
                       vlc_value_t oldval, vlc_value_t newval, void *p_data );

60
static void libvlc_media_player_destroy( libvlc_media_player_t *p_mi );
61

62
/*
63
 * Release the associated input thread.
64 65
 *
 * Object lock is NOT held.
66
 */
67
static void release_input_thread( libvlc_media_player_t *p_mi, bool b_input_abort )
68
{
69
    input_thread_t * p_input_thread;
70

71
    if( !p_mi || !p_mi->p_input_thread )
72
        return;
73

74
    p_input_thread = p_mi->p_input_thread;
75

76 77 78 79 80 81 82 83 84
    var_DelCallback( p_input_thread, "can-seek",
                     input_seekable_changed, p_mi );
    var_DelCallback( p_input_thread, "can-pause",
                    input_pausable_changed, p_mi );
    var_DelCallback( p_input_thread, "intf-event",
                     input_event_changed, p_mi );

    /* We owned this one */
    input_Stop( p_input_thread, b_input_abort );
85

86 87
    vlc_thread_join( p_input_thread );

88 89 90 91 92
    assert( p_mi->p_input_resource == NULL );
    assert( p_input_thread->b_dead );
    /* Store the input resource for future use. */
    p_mi->p_input_resource = input_DetachResource( p_input_thread );

93 94 95
    var_Destroy( p_input_thread, "drawable-hwnd" );
    var_Destroy( p_input_thread, "drawable-xid" );
    var_Destroy( p_input_thread, "drawable-agl" );
96 97

    vlc_object_release( p_input_thread );
98 99

    p_mi->p_input_thread = NULL;
100 101
}

102 103
/*
 * Retrieve the input thread. Be sure to release the object
104
 * once you are done with it. (libvlc Internal)
105
 *
106
 * Function will lock the object.
107
 */
108
input_thread_t *libvlc_get_input_thread( libvlc_media_player_t *p_mi,
109
                                         libvlc_exception_t *p_e )
110 111 112
{
    input_thread_t *p_input_thread;

113
    if( !p_mi ) RAISENULL( "Media Instance is NULL" );
114

115 116
    vlc_mutex_lock( &p_mi->object_lock );

117
    if( !p_mi->p_input_thread )
118 119
    {
        vlc_mutex_unlock( &p_mi->object_lock );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
120
        RAISENULL( "Input is NULL" );
121
    }
122

123
    p_input_thread = p_mi->p_input_thread;
124
    vlc_object_hold( p_input_thread );
125

126
    vlc_mutex_unlock( &p_mi->object_lock );
127

128 129 130
    return p_input_thread;
}

131
static int
132
input_seekable_changed( vlc_object_t * p_this, char const * psz_cmd,
133 134 135 136
                        vlc_value_t oldval, vlc_value_t newval,
                        void * p_userdata )
{
    VLC_UNUSED(oldval);
137 138
    VLC_UNUSED(p_this);
    VLC_UNUSED(psz_cmd);
139
    libvlc_media_player_t * p_mi = p_userdata;
140 141
    libvlc_event_t event;

142
    libvlc_media_set_state( p_mi->p_md, libvlc_NothingSpecial, NULL);
143
    event.type = libvlc_MediaPlayerSeekableChanged;
144
    event.u.media_player_seekable_changed.new_seekable = newval.b_bool;
145 146 147 148 149 150 151 152 153 154 155

    libvlc_event_send( p_mi->p_event_manager, &event );
    return VLC_SUCCESS;
}

static int
input_pausable_changed( vlc_object_t * p_this, char const * psz_cmd,
                        vlc_value_t oldval, vlc_value_t newval,
                        void * p_userdata )
{
    VLC_UNUSED(oldval);
156 157
    VLC_UNUSED(p_this);
    VLC_UNUSED(psz_cmd);
158
    libvlc_media_player_t * p_mi = p_userdata;
159 160
    libvlc_event_t event;

161
    libvlc_media_set_state( p_mi->p_md, libvlc_NothingSpecial, NULL);
162
    event.type = libvlc_MediaPlayerPausableChanged;
163
    event.u.media_player_pausable_changed.new_pausable = newval.b_bool;
164 165 166 167 168

    libvlc_event_send( p_mi->p_event_manager, &event );
    return VLC_SUCCESS;
}

169
static int
Laurent Aimar's avatar
Laurent Aimar committed
170
input_event_changed( vlc_object_t * p_this, char const * psz_cmd,
171 172 173
                     vlc_value_t oldval, vlc_value_t newval,
                     void * p_userdata )
{
174
    VLC_UNUSED(oldval);
Laurent Aimar's avatar
Laurent Aimar committed
175
    input_thread_t * p_input = (input_thread_t *)p_this;
176
    libvlc_media_player_t * p_mi = p_userdata;
Laurent Aimar's avatar
Laurent Aimar committed
177
    libvlc_event_t event;
178

179
    assert( !strcmp( psz_cmd, "intf-event" ) );
180

181 182
    if( newval.i_int == INPUT_EVENT_STATE )
    {
183
        libvlc_state_t libvlc_state;
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215

        switch ( var_GetInteger( p_input, "state" ) )
        {
            case INIT_S:
                libvlc_state = libvlc_NothingSpecial;
                event.type = libvlc_MediaPlayerNothingSpecial;
                break;
            case OPENING_S:
                libvlc_state = libvlc_Opening;
                event.type = libvlc_MediaPlayerOpening;
                break;
            case PLAYING_S:
                libvlc_state = libvlc_Playing;
                event.type = libvlc_MediaPlayerPlaying;
                break;
            case PAUSE_S:
                libvlc_state = libvlc_Paused;
                event.type = libvlc_MediaPlayerPaused;
                break;
            case END_S:
                libvlc_state = libvlc_Ended;
                event.type = libvlc_MediaPlayerEndReached;
                break;
            case ERROR_S:
                libvlc_state = libvlc_Error;
                event.type = libvlc_MediaPlayerEncounteredError;
                break;

            default:
                return VLC_SUCCESS;
        }

216
        libvlc_media_set_state( p_mi->p_md, libvlc_state, NULL );
217 218
        libvlc_event_send( p_mi->p_event_manager, &event );
    }
219 220 221 222 223 224 225 226
    else if( newval.i_int == INPUT_EVENT_ABORT )
    {
        libvlc_state_t libvlc_state = libvlc_Stopped;
        event.type = libvlc_MediaPlayerStopped;

        libvlc_media_set_state( p_mi->p_md, libvlc_state, NULL );
        libvlc_event_send( p_mi->p_event_manager, &event );
    }
227
    else if( newval.i_int == INPUT_EVENT_POSITION )
228 229 230 231 232 233
    {
        if( var_GetInteger( p_input, "state" ) != PLAYING_S )
            return VLC_SUCCESS; /* Don't send the position while stopped */

        /* */
        event.type = libvlc_MediaPlayerPositionChanged;
234 235
        event.u.media_player_position_changed.new_position =
                                          var_GetFloat( p_input, "position" );
236 237 238 239
        libvlc_event_send( p_mi->p_event_manager, &event );

        /* */
        event.type = libvlc_MediaPlayerTimeChanged;
240 241
        event.u.media_player_time_changed.new_time =
                                               var_GetTime( p_input, "time" );
242 243
        libvlc_event_send( p_mi->p_event_manager, &event );
    }
244 245 246 247 248 249 250
    else if( newval.i_int == INPUT_EVENT_LENGTH )
    {
        event.type = libvlc_MediaPlayerLengthChanged;
        event.u.media_player_length_changed.new_length =
                                               var_GetTime( p_input, "length" );
        libvlc_event_send( p_mi->p_event_manager, &event );
    }
Laurent Aimar's avatar
Laurent Aimar committed
251

252
    return VLC_SUCCESS;
Laurent Aimar's avatar
Laurent Aimar committed
253

254 255
}

256 257
static void libvlc_media_player_destroy( libvlc_media_player_t * );

258
/**************************************************************************
259 260 261 262 263 264 265 266 267 268 269 270
 * Create a Media Instance object.
 *
 * Refcount strategy:
 * - All items created by _new start with a refcount set to 1.
 * - Accessor _release decrease the refcount by 1, if after that
 *   operation the refcount is 0, the object is destroyed.
 * - Accessor _retain increase the refcount by 1 (XXX: to implement)
 *
 * Object locking strategy:
 * - No lock held while in constructor.
 * - When accessing any member variable this lock is held. (XXX who locks?)
 * - When attempting to destroy the object the lock is also held.
271
 **************************************************************************/
272 273
libvlc_media_player_t *
libvlc_media_player_new( libvlc_instance_t * p_libvlc_instance,
274
                           libvlc_exception_t * p_e )
275
{
276
    libvlc_media_player_t * p_mi;
277

278
    assert( p_libvlc_instance );
279

280
    p_mi = malloc( sizeof(libvlc_media_player_t) );
281 282
    if( !p_mi )
    {
283 284
        libvlc_exception_raise( p_e );
        libvlc_printerr( "Not enough memory" );
285 286
        return NULL;
    }
287
    p_mi->p_md = NULL;
288
    p_mi->drawable.agl = 0;
289 290
    p_mi->drawable.xid = 0;
    p_mi->drawable.hwnd = NULL;
291
    p_mi->drawable.nsobject = NULL;
292
    p_mi->p_libvlc_instance = p_libvlc_instance;
293
    p_mi->p_input_thread = NULL;
294
    p_mi->p_input_resource = NULL;
295
    p_mi->i_refcount = 1;
296
    vlc_mutex_init( &p_mi->object_lock );
297 298 299 300
    p_mi->p_event_manager = libvlc_event_manager_new( p_mi,
            p_libvlc_instance, p_e );
    if( libvlc_exception_raised( p_e ) )
    {
301
        vlc_mutex_destroy( &p_mi->object_lock );
302 303 304
        free( p_mi );
        return NULL;
    }
305 306

    libvlc_event_manager_register_event_type( p_mi->p_event_manager,
307
            libvlc_MediaPlayerNothingSpecial, p_e );
308
    libvlc_event_manager_register_event_type( p_mi->p_event_manager,
309
            libvlc_MediaPlayerOpening, p_e );
310
    libvlc_event_manager_register_event_type( p_mi->p_event_manager,
311 312 313
            libvlc_MediaPlayerBuffering, p_e );
    libvlc_event_manager_register_event_type( p_mi->p_event_manager,
            libvlc_MediaPlayerPlaying, p_e );
314
    libvlc_event_manager_register_event_type( p_mi->p_event_manager,
315
            libvlc_MediaPlayerPaused, p_e );
316
    libvlc_event_manager_register_event_type( p_mi->p_event_manager,
317
            libvlc_MediaPlayerStopped, p_e );
318 319 320 321
    libvlc_event_manager_register_event_type( p_mi->p_event_manager,
            libvlc_MediaPlayerForward, p_e );
    libvlc_event_manager_register_event_type( p_mi->p_event_manager,
            libvlc_MediaPlayerBackward, p_e );
322 323
    libvlc_event_manager_register_event_type( p_mi->p_event_manager,
            libvlc_MediaPlayerEndReached, p_e );
324 325 326
    libvlc_event_manager_register_event_type( p_mi->p_event_manager,
            libvlc_MediaPlayerEncounteredError, p_e );

327
    libvlc_event_manager_register_event_type( p_mi->p_event_manager,
328
            libvlc_MediaPlayerPositionChanged, p_e );
329
    libvlc_event_manager_register_event_type( p_mi->p_event_manager,
330
            libvlc_MediaPlayerTimeChanged, p_e );
331 332
    libvlc_event_manager_register_event_type( p_mi->p_event_manager,
            libvlc_MediaPlayerLengthChanged, p_e );
333
    libvlc_event_manager_register_event_type( p_mi->p_event_manager,
334
            libvlc_MediaPlayerTitleChanged, p_e );
335
    libvlc_event_manager_register_event_type( p_mi->p_event_manager,
336
            libvlc_MediaPlayerSeekableChanged, p_e );
337
    libvlc_event_manager_register_event_type( p_mi->p_event_manager,
338
            libvlc_MediaPlayerPausableChanged, p_e );
339

340 341 342
    /* Snapshot initialization */
    libvlc_event_manager_register_event_type( p_mi->p_event_manager,
           libvlc_MediaPlayerSnapshotTaken, p_e );
343

344 345 346 347
    /* Attach a var callback to the global object to provide the glue between
        vout_thread that generates the event and media_player that re-emits it
        with its own event manager
    */
348
    var_AddCallback( p_libvlc_instance->p_libvlc_int, "snapshot-file",
349
                     SnapshotTakenCallback, p_mi );
350

351 352 353 354
    return p_mi;
}

/**************************************************************************
355
 * Create a Media Instance object with a media descriptor.
356
 **************************************************************************/
357 358 359
libvlc_media_player_t *
libvlc_media_player_new_from_media(
                                    libvlc_media_t * p_md,
360
                                    libvlc_exception_t *p_e )
361
{
362
    libvlc_media_player_t * p_mi;
363

364
    p_mi = libvlc_media_player_new( p_md->p_libvlc_instance, p_e );
365
    if( !p_mi )
366 367
        return NULL;

368
    libvlc_media_retain( p_md );
369
    p_mi->p_md = p_md;
370 371 372 373 374

    return p_mi;
}

/**************************************************************************
375
 * Destroy a Media Instance object (libvlc internal)
376
 *
377
 * Warning: No lock held here, but hey, this is internal. Caller must lock.
378
 **************************************************************************/
379
static void libvlc_media_player_destroy( libvlc_media_player_t *p_mi )
380
{
381
    assert( p_mi );
382

383
    /* Detach Callback from the main libvlc object */
384
    var_DelCallback( p_mi->p_libvlc_instance->p_libvlc_int,
385
                     "snapshot-file", SnapshotTakenCallback, p_mi );
386

387
    /* Release the input thread */
388
    release_input_thread( p_mi, true );
389

390 391 392 393 394 395
    if( p_mi->p_input_resource )
    {
        input_resource_Delete( p_mi->p_input_resource );
        p_mi->p_input_resource = NULL;    
    }

396
    libvlc_event_manager_release( p_mi->p_event_manager );
397
    libvlc_media_release( p_mi->p_md );
398
    vlc_mutex_destroy( &p_mi->object_lock );
399 400 401 402
    free( p_mi );
}

/**************************************************************************
403 404 405
 * Release a Media Instance object.
 *
 * Function does the locking.
406
 **************************************************************************/
407
void libvlc_media_player_release( libvlc_media_player_t *p_mi )
408
{
409
    bool destroy;
410

411
    assert( p_mi );
412
    vlc_mutex_lock( &p_mi->object_lock );
413
    destroy = !--p_mi->i_refcount;
414
    vlc_mutex_unlock( &p_mi->object_lock );
415

416 417
    if( destroy )
        libvlc_media_player_destroy( p_mi );
418
}
419

420
/**************************************************************************
421 422 423
 * Retain a Media Instance object.
 *
 * Caller must hold the lock.
424
 **************************************************************************/
425
void libvlc_media_player_retain( libvlc_media_player_t *p_mi )
426
{
427
    assert( p_mi );
428

429
    vlc_mutex_lock( &p_mi->object_lock );
430
    p_mi->i_refcount++;
431
    vlc_mutex_unlock( &p_mi->object_lock );
432
}
433

434
/**************************************************************************
435 436 437
 * Set the Media descriptor associated with the instance.
 *
 * Enter without lock -- function will lock the object.
438
 **************************************************************************/
439 440 441
void libvlc_media_player_set_media(
                            libvlc_media_player_t *p_mi,
                            libvlc_media_t *p_md,
442 443
                            libvlc_exception_t *p_e )
{
444
    VLC_UNUSED(p_e);
445 446 447 448

    if( !p_mi )
        return;

449
    vlc_mutex_lock( &p_mi->object_lock );
450

451 452 453 454 455 456
    /* FIXME I am not sure if it is a user request or on die(eof/error)
     * request here */
    release_input_thread( p_mi,
                          p_mi->p_input_thread &&
                          !p_mi->p_input_thread->b_eof &&
                          !p_mi->p_input_thread->b_error );
457

458
    if( p_mi->p_md )
459
        libvlc_media_set_state( p_mi->p_md, libvlc_NothingSpecial, p_e );
460

461
    libvlc_media_release( p_mi->p_md );
462

463 464 465
    if( !p_md )
    {
        p_mi->p_md = NULL;
466
        vlc_mutex_unlock( &p_mi->object_lock );
467 468 469
        return; /* It is ok to pass a NULL md */
    }

470
    libvlc_media_retain( p_md );
471
    p_mi->p_md = p_md;
472

473 474 475 476
    /* The policy here is to ignore that we were created using a different
     * libvlc_instance, because we don't really care */
    p_mi->p_libvlc_instance = p_md->p_libvlc_instance;

477
    vlc_mutex_unlock( &p_mi->object_lock );
478 479 480
}

/**************************************************************************
481
 * Get the Media descriptor associated with the instance.
482
 **************************************************************************/
483 484 485
libvlc_media_t *
libvlc_media_player_get_media(
                            libvlc_media_player_t *p_mi,
486 487
                            libvlc_exception_t *p_e )
{
488
    libvlc_media_t *p_m;
489
    VLC_UNUSED(p_e);
490

491 492 493 494 495
    vlc_mutex_lock( &p_mi->object_lock );
    p_m = p_mi->p_md;
    if( p_m )
        libvlc_media_retain( p_mi->p_md );
    vlc_mutex_unlock( &p_mi->object_lock );
496
    return p_mi->p_md;
497 498
}

499
/**************************************************************************
500
 * Get the event Manager.
501 502
 **************************************************************************/
libvlc_event_manager_t *
503 504
libvlc_media_player_event_manager(
                            libvlc_media_player_t *p_mi,
505 506
                            libvlc_exception_t *p_e )
{
507
    VLC_UNUSED(p_e);
508 509 510 511

    return p_mi->p_event_manager;
}

512
/**************************************************************************
513
 * Trigger a snapshot Taken Event.
514 515 516 517 518 519 520 521 522 523 524 525 526
 *************************************************************************/
static int SnapshotTakenCallback( vlc_object_t *p_this, char const *psz_cmd,
                       vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
    VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
    VLC_UNUSED(p_this) ;

    libvlc_media_player_t* p_mi = (libvlc_media_player_t*) p_data ;
    libvlc_event_t event ;
    event.type = libvlc_MediaPlayerSnapshotTaken ;
    event.u.media_player_snapshot_taken.psz_filename = newval.psz_string ;
    /* Snapshot psz data is a vlc_variable owned by libvlc object .
         Its memmory management is taken care by the obj*/
527 528 529
    msg_Dbg( p_this, "about to emit libvlc_snapshot_taken.make psz_str=0x%p"
             " (%s)", event.u.media_player_snapshot_taken.psz_filename,
             event.u.media_player_snapshot_taken.psz_filename );
530 531 532 533 534
    libvlc_event_send( p_mi->p_event_manager, &event );

    return VLC_SUCCESS;
}

535
/**************************************************************************
536
 * Tell media player to start playing.
537
 **************************************************************************/
538
void libvlc_media_player_play( libvlc_media_player_t *p_mi,
539 540 541 542
                                 libvlc_exception_t *p_e )
{
    input_thread_t * p_input_thread;

543
    if( (p_input_thread = libvlc_get_input_thread( p_mi, p_e )) )
544
    {
Sam Hocevar's avatar
Sam Hocevar committed
545
        /* A thread already exists, send it a play message */
546
        input_Control( p_input_thread, INPUT_SET_STATE, PLAYING_S );
547
        vlc_object_release( p_input_thread );
548 549 550
        return;
    }

551 552 553
    /* Ignore previous exception */
    libvlc_exception_clear( p_e );

554
    vlc_mutex_lock( &p_mi->object_lock );
555

556 557
    if( !p_mi->p_md )
    {
558
        vlc_mutex_unlock( &p_mi->object_lock );
559 560
        libvlc_exception_raise( p_e );
        libvlc_printerr( "No associated media descriptor" );
561 562 563
        return;
    }

564
    p_mi->p_input_thread = input_Create( p_mi->p_libvlc_instance->p_libvlc_int,
565
                                         p_mi->p_md->p_input_item, NULL, p_mi->p_input_resource );
566

567
    if( !p_mi->p_input_thread )
568 569
    {
        vlc_mutex_unlock( &p_mi->object_lock );
570
        return;
571
    }
572

573
    p_mi->p_input_resource = NULL;
574 575
    p_input_thread = p_mi->p_input_thread;

576 577 578 579
    var_Create( p_input_thread, "drawable-agl", VLC_VAR_INTEGER );
    if( p_mi->drawable.agl )
        var_SetInteger( p_input_thread, "drawable-agl", p_mi->drawable.agl );

580
    var_Create( p_input_thread, "drawable-xid", VLC_VAR_INTEGER );
581 582
    if( p_mi->drawable.xid )
        var_SetInteger( p_input_thread, "drawable-xid", p_mi->drawable.xid );
583

584 585
    var_Create( p_input_thread, "drawable-hwnd", VLC_VAR_ADDRESS );
    if( p_mi->drawable.hwnd != NULL )
586
        var_SetAddress( p_input_thread, "drawable-hwnd", p_mi->drawable.hwnd );
587

Rémi Duraffort's avatar
Rémi Duraffort committed
588
    var_Create( p_input_thread, "drawable-nsobject", VLC_VAR_ADDRESS );
589
    if( p_mi->drawable.nsobject != NULL )
590
        var_SetAddress( p_input_thread, "drawable-nsobject", p_mi->drawable.nsobject );
Rémi Duraffort's avatar
Rémi Duraffort committed
591

592 593
    var_AddCallback( p_input_thread, "can-seek", input_seekable_changed, p_mi );
    var_AddCallback( p_input_thread, "can-pause", input_pausable_changed, p_mi );
Laurent Aimar's avatar
Laurent Aimar committed
594
    var_AddCallback( p_input_thread, "intf-event", input_event_changed, p_mi );
595

596 597 598 599 600 601
    if( input_Start( p_input_thread ) )
    {
        vlc_object_release( p_input_thread );
        p_mi->p_input_thread = NULL;
    }

602
    vlc_mutex_unlock( &p_mi->object_lock );
603 604 605
}

/**************************************************************************
606
 * Pause.
607
 **************************************************************************/
608
void libvlc_media_player_pause( libvlc_media_player_t *p_mi,
609 610
                                  libvlc_exception_t *p_e )
{
611
    input_thread_t * p_input_thread = libvlc_get_input_thread( p_mi, p_e );
612
    if( !p_input_thread )
613 614
        return;

615
    libvlc_state_t state = libvlc_media_player_get_state( p_mi, p_e );
616
    if( state == libvlc_Playing || state == libvlc_Buffering )
617
    {
618
        if( libvlc_media_player_can_pause( p_mi, p_e ) )
619 620
            input_Control( p_input_thread, INPUT_SET_STATE, PAUSE_S );
        else
621
            libvlc_media_player_stop( p_mi, p_e );
622
    }
623
    else
624 625
        input_Control( p_input_thread, INPUT_SET_STATE, PLAYING_S );

626
    vlc_object_release( p_input_thread );
627
}
628

629
/**************************************************************************
630 631 632
 * Tells whether the media player is currently playing.
 *
 * Enter with lock held.
633 634 635 636
 **************************************************************************/
int libvlc_media_player_is_playing( libvlc_media_player_t *p_mi,
                                     libvlc_exception_t *p_e )
{
637
    libvlc_state_t state = libvlc_media_player_get_state( p_mi, p_e );
638
    return (libvlc_Playing == state) || (libvlc_Buffering == state);
639 640
}

641
/**************************************************************************
642
 * Stop playing.
643
 **************************************************************************/
644
void libvlc_media_player_stop( libvlc_media_player_t *p_mi,
645 646
                                 libvlc_exception_t *p_e )
{
647
    libvlc_state_t state = libvlc_media_player_get_state( p_mi, p_e );
648

649 650 651
    if( state == libvlc_Playing ||
        state == libvlc_Paused ||
        state == libvlc_Buffering )
652
    {
653 654
        /* Send a stop notification event only if we are in playing,
         * buffering or paused states */
655
        libvlc_media_set_state( p_mi->p_md, libvlc_Stopped, p_e );
656 657 658

        /* Construct and send the event */
        libvlc_event_t event;
659
        event.type = libvlc_MediaPlayerStopped;
660 661 662
        libvlc_event_send( p_mi->p_event_manager, &event );
    }

663 664 665
    vlc_mutex_lock( &p_mi->object_lock );
    release_input_thread( p_mi, true ); /* This will stop the input thread */
    vlc_mutex_unlock( &p_mi->object_lock );
666 667
}

668 669 670 671
/**************************************************************************
 * set_nsobject
 **************************************************************************/
void libvlc_media_player_set_nsobject( libvlc_media_player_t *p_mi,
Jean-Paul Saman's avatar
Jean-Paul Saman committed
672 673
                                        void * drawable,
                                        libvlc_exception_t *p_e )
674 675 676 677 678 679
{
    (void) p_e;
    p_mi->drawable.nsobject = drawable;
}

/**************************************************************************
680
 * get_nsobject
681
 **************************************************************************/
Pierre's avatar
Pierre committed
682
void * libvlc_media_player_get_nsobject( libvlc_media_player_t *p_mi )
683 684 685 686
{
    return p_mi->drawable.nsobject;
}

687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705
/**************************************************************************
 * set_agl
 **************************************************************************/
void libvlc_media_player_set_agl( libvlc_media_player_t *p_mi,
                                      uint32_t drawable,
                                      libvlc_exception_t *p_e )
{
    (void) p_e;
    p_mi->drawable.agl = drawable;
}

/**************************************************************************
 * get_agl
 **************************************************************************/
uint32_t libvlc_media_player_get_agl( libvlc_media_player_t *p_mi )
{
    return p_mi->drawable.agl;
}

706 707 708
/**************************************************************************
 * set_xwindow
 **************************************************************************/
709 710 711
void libvlc_media_player_set_xwindow( libvlc_media_player_t *p_mi,
                                      uint32_t drawable,
                                      libvlc_exception_t *p_e )
712 713 714 715 716
{
    (void) p_e;
    p_mi->drawable.xid = drawable;
}

717 718 719
/**************************************************************************
 * get_xwindow
 **************************************************************************/
720 721 722 723 724
uint32_t libvlc_media_player_get_xwindow( libvlc_media_player_t *p_mi )
{
    return p_mi->drawable.xid;
}

725 726 727
/**************************************************************************
 * set_hwnd
 **************************************************************************/
728 729 730 731 732 733 734 735
void libvlc_media_player_set_hwnd( libvlc_media_player_t *p_mi,
                                   void *drawable,
                                   libvlc_exception_t *p_e )
{
    (void) p_e;
    p_mi->drawable.hwnd = drawable;
}

736 737 738
/**************************************************************************
 * get_hwnd
 **************************************************************************/
739 740 741 742 743
void *libvlc_media_player_get_hwnd( libvlc_media_player_t *p_mi )
{
    return p_mi->drawable.hwnd;
}

744 745 746
/**************************************************************************
 * Getters for stream information
 **************************************************************************/
747 748
libvlc_time_t libvlc_media_player_get_length(
                             libvlc_media_player_t *p_mi,
749
                             libvlc_exception_t *p_e )
750 751
{
    input_thread_t *p_input_thread;
752
    libvlc_time_t i_time;
753

754
    p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
755
    if( !p_input_thread )
Jean-Paul Saman's avatar
Jean-Paul Saman committed
756 757
        return -1;

758
    i_time = var_GetTime( p_input_thread, "length" );
759 760
    vlc_object_release( p_input_thread );

761
    return (i_time+500LL)/1000LL;
762 763
}

764 765
libvlc_time_t libvlc_media_player_get_time(
                                   libvlc_media_player_t *p_mi,
766
                                   libvlc_exception_t *p_e )
767 768
{
    input_thread_t *p_input_thread;
769
    libvlc_time_t i_time;
770

771
    p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
772
    if( !p_input_thread )
Jean-Paul Saman's avatar
Jean-Paul Saman committed
773
        return -1;
774

775
    i_time = var_GetTime( p_input_thread , "time" );
776
    vlc_object_release( p_input_thread );
777
    return (i_time+500LL)/1000LL;
778 779
}

780 781
void libvlc_media_player_set_time(
                                 libvlc_media_player_t *p_mi,
782
                                 libvlc_time_t i_time,
783
                                 libvlc_exception_t *p_e )
784 785 786
{
    input_thread_t *p_input_thread;

787
    p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
788
    if( !p_input_thread )
Jean-Paul Saman's avatar
Jean-Paul Saman committed
789 790
        return;

791
    var_SetTime( p_input_thread, "time", i_time*1000LL );
792 793 794
    vlc_object_release( p_input_thread );
}

795 796
void libvlc_media_player_set_position(
                                libvlc_media_player_t *p_mi,
797
                                float position,
798
                                libvlc_exception_t *p_e )
799 800
{
    input_thread_t *p_input_thread;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
801

802
    p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
803
    if( !p_input_thread )
Jean-Paul Saman's avatar
Jean-Paul Saman committed
804
        return;
805

806
    var_SetFloat( p_input_thread, "position", position );
807 808 809
    vlc_object_release( p_input_thread );
}

810 811
float libvlc_media_player_get_position(
                                 libvlc_media_player_t *p_mi,
812
                                 libvlc_exception_t *p_e )
813 814
{
    input_thread_t *p_input_thread;
815
    float f_position;
816

817
    p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
818
    if( !p_input_thread )
Jean-Paul Saman's avatar
Jean-Paul Saman committed
819
        return -1.0;
820

821
    f_position = var_GetFloat( p_input_thread, "position" );
822 823
    vlc_object_release( p_input_thread );

824
    return f_position;
825
}
Filippo Carone's avatar
Filippo Carone committed
826

827 828
void libvlc_media_player_set_chapter(
                                 libvlc_media_player_t *p_mi,
829 830 831 832 833 834 835 836 837
                                 int chapter,
                                 libvlc_exception_t *p_e )
{
    input_thread_t *p_input_thread;

    p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
    if( !p_input_thread )
        return;

838
    var_SetInteger( p_input_thread, "chapter", chapter );
839 840 841
    vlc_object_release( p_input_thread );
}

842 843
int libvlc_media_player_get_chapter(
                                 libvlc_media_player_t *p_mi,
844 845 846
                                 libvlc_exception_t *p_e )
{
    input_thread_t *p_input_thread;
847
    int i_chapter;
848 849 850

    p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
    if( !p_input_thread )
851
        return -1;
852

853
    i_chapter = var_GetInteger( p_input_thread, "chapter" );
854 855
    vlc_object_release( p_input_thread );

856
    return i_chapter;
857 858
}

859 860
int libvlc_media_player_get_chapter_count(
                                 libvlc_media_player_t *p_mi,
861 862 863 864 865 866 867
                                 libvlc_exception_t *p_e )
{
    input_thread_t *p_input_thread;
    vlc_value_t val;

    p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
    if( !p_input_thread )
868
        return -1;
869 870 871 872 873 874 875

    var_Change( p_input_thread, "chapter", VLC_VAR_CHOICESCOUNT, &val, NULL );
    vlc_object_release( p_input_thread );

    return val.i_int;
}

876 877 878 879 880 881 882 883 884 885 886 887
int libvlc_media_player_get_chapter_count_for_title(
                                 libvlc_media_player_t *p_mi,
                                 int i_title,
                                 libvlc_exception_t *p_e )
{
    input_thread_t *p_input_thread;
    vlc_value_t val;

    p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
    if( !p_input_thread )
        return -1;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
888 889
    char *psz_name;
    if( asprintf( &psz_name,  "title %2i", i_title ) == -1 )
890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911
    {
        vlc_object_release( p_input_thread );
        return -1;
    }
    var_Change( p_input_thread, psz_name, VLC_VAR_CHOICESCOUNT, &val, NULL );
    vlc_object_release( p_input_thread );
    free( psz_name );

    return val.i_int;
}

void libvlc_media_player_set_title(
                                 libvlc_media_player_t *p_mi,
                                 int i_title,
                                 libvlc_exception_t *p_e )
{
    input_thread_t *p_input_thread;

    p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
    if( !p_input_thread )
        return;

912
    var_SetInteger( p_input_thread, "title", i_title );
913 914 915 916 917 918 919 920 921 922 923 924 925 926
    vlc_object_release( p_input_thread );

    //send event
    libvlc_event_t event;
    event.type = libvlc_MediaPlayerTitleChanged;
    event.u.media_player_title_changed.new_title = i_title;
    libvlc_event_send( p_mi->p_event_manager, &event );
}

int libvlc_media_player_get_title(
                                 libvlc_media_player_t *p_mi,
                                 libvlc_exception_t *p_e )
{
    input_thread_t *p_input_thread;
927
    int i_title;
928 929 930 931 932

    p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
    if( !p_input_thread )
        return -1;

933
    i_title = var_GetInteger( p_input_thread, "title" );
934 935
    vlc_object_release( p_input_thread );

936
    return i_title;
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 964 965 966
}

int libvlc_media_player_get_title_count(
                                 libvlc_media_player_t *p_mi,
                                 libvlc_exception_t *p_e )
{
    input_thread_t *p_input_thread;
    vlc_value_t val;

    p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
    if( !p_input_thread )
        return -1;

    var_Change( p_input_thread, "title", VLC_VAR_CHOICESCOUNT, &val, NULL );
    vlc_object_release( p_input_thread );

    return val.i_int;
}

void libvlc_media_player_next_chapter(
                                 libvlc_media_player_t *p_mi,
                                 libvlc_exception_t *p_e )
{
    input_thread_t *p_input_thread;

    p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
    if( !p_input_thread )
        return;

    int i_type = var_Type( p_input_thread, "next-chapter" );
967 968
    var_SetBool( p_input_thread, (i_type & VLC_VAR_TYPE) != 0 ?
                            "next-chapter":"next-title", true );
969 970 971 972 973 974 975 976 977 978 979 980 981 982 983

    vlc_object_release( p_input_thread );
}

void libvlc_media_player_previous_chapter(
                                 libvlc_media_player_t *p_mi,
                                 libvlc_exception_t *p_e )
{
    input_thread_t *p_input_thread;

    p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
    if( !p_input_thread )
        return;

    int i_type = var_Type( p_input_thread, "next-chapter" );
984 985
    var_SetBool( p_input_thread, (i_type & VLC_VAR_TYPE) != 0 ?
                            "prev-chapter":"prev-title", true );
986 987 988 989

    vlc_object_release( p_input_thread );
}

990 991
float libvlc_media_player_get_fps(
                                 libvlc_media_player_t *p_mi,
992
                                 libvlc_exception_t *p_e)
993
{
994
    input_thread_t *p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
995
    double f_fps = 0.0;
996

997
    if( p_input_thread )
998
    {
999 1000
        if( input_Control( p_input_thread, INPUT_GET_VIDEO_FPS, &f_fps ) )
            f_fps = 0.0;
1001 1002
        vlc_object_release( p_input_thread );
    }
1003
    return f_fps;
1004 1005
}

1006
int libvlc_media_player_will_play( libvlc_media_player_t *p_mi,
1007
                                     libvlc_exception_t *p_e)
Filippo Carone's avatar
Filippo Carone committed
1008
{
1009
    input_thread_t *p_input_thread =
1010
                            libvlc_get_input_thread ( p_mi, p_e);
1011
    if ( !p_input_thread )
1012
        return false;
Filippo Carone's avatar
Filippo Carone committed
1013

1014
    if ( !p_input_thread->b_die && !p_input_thread->b_dead )
Filippo Carone's avatar
Filippo Carone committed
1015
    {
1016
        vlc_object_release( p_input_thread );
1017
        return true;
Filippo Carone's avatar
Filippo Carone committed
1018
    }
1019
    vlc_object_release( p_input_thread );
1020
    return false;
Filippo Carone's avatar
Filippo Carone committed
1021
}
1022

1023 1024
void libvlc_media_player_set_rate(
                                 libvlc_media_player_t *p_mi,
1025
                                 float rate,
1026
                                 libvlc_exception_t *p_e )
1027 1028
{
    input_thread_t *p_input_thread;
1029
    bool b_can_rewind;
1030

1031
    p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1032
    if( !p_input_thread )
Jean-Paul Saman's avatar
Jean-Paul Saman committed
1033
        return;
1034

1035
    b_can_rewind = var_GetBool( p_input_thread, "can-rewind" );
1036
    if( (rate < 0.0) && !b_can_rewind )
1037 1038
    {
        vlc_object_release( p_input_thread );
1039 1040
        libvlc_exception_raise( p_e );
        libvlc_printerr( "Invalid playback rate" );
1041 1042 1043
        return;
    }

1044
    var_SetInteger( p_input_thread, "rate", 1000.0f/rate );
1045 1046 1047
    vlc_object_release( p_input_thread );
}

1048 1049
float libvlc_media_player_get_rate(
                                 libvlc_media_player_t *p_mi,
1050 1051 1052
                                 libvlc_exception_t *p_e )
{
    input_thread_t *p_input_thread;
1053
    int i_rate;
1054
    bool b_can_rewind;
1055

1056 1057 1058
    p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
    if( !p_input_thread )
        return 0.0;  /* rate < 0 indicates rewind */
1059

1060
    i_rate = var_GetInteger( p_input_thread, "rate" );
1061
    b_can_rewind = var_GetBool( p_input_thread, "can-rewind" );
1062
    if( i_rate < 0 && !b_can_rewind )
1063
    {
1064
        vlc_object_release( p_input_thread );
1065 1066
        return 0.0;
    }
1067 1068
    vlc_object_release( p_input_thread );

1069
    return (float)1000.0f/i_rate;
1070 1071
}

1072
libvlc_state_t libvlc_media_player_get_state(
1073
                                 libvlc_media_player_t *p_mi,
1074 1075 1076
                                 libvlc_exception_t *p_e )
{
    input_thread_t *p_input_thread;
1077
    libvlc_state_t state = libvlc_Ended;
1078

1079
    p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1080
    if( !p_input_thread )
1081 1082 1083 1084
    {
        /* We do return the right value, no need to throw an exception */
        if( libvlc_exception_raised( p_e ) )
            libvlc_exception_clear( p_e );
1085
        return state;
1086
    }
1087

1088
    state = libvlc_media_get_state( p_mi->p_md, NULL );
1089
    if( state == libvlc_Playing )
1090 1091 1092
    {
        float caching;
        caching = var_GetFloat( p_input_thread, "cache" );
1093
        if( caching > 0.0 && caching < 1.0 )
1094 1095 1096 1097
            state = libvlc_Buffering;
    }
    vlc_object_release( p_input_thread );
    return state;
1098
}
1099

1100
int libvlc_media_player_is_seekable( libvlc_media_player_t *p_mi,
1101
                                       libvlc_exception_t *p_e )
1102 1103
{
    input_thread_t *p_input_thread;
1104
    bool b_seekable;
1105 1106 1107 1108 1109 1110 1111

    p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
    if ( !p_input_thread )
    {
        /* We do return the right value, no need to throw an exception */
        if( libvlc_exception_raised( p_e ) )
            libvlc_exception_clear( p_e );
1112
        return false;
1113
    }
1114
    b_seekable = var_GetBool( p_input_thread, "can-seek" );
1115 1116
    vlc_object_release( p_input_thread );

1117
    return b_seekable;
1118
}
1119

1120 1121 1122 1123 1124 1125 1126
/* internal function, used by audio, video */
libvlc_track_description_t *
        libvlc_get_track_description( libvlc_media_player_t *p_mi,
                                      const char *psz_variable,
                                      libvlc_exception_t *p_e )
{
    input_thread_t *p_input = libvlc_get_input_thread( p_mi, p_e );
Rémi Duraffort's avatar
Rémi Duraffort committed
1127 1128
    libvlc_track_description_t *p_track_description = NULL,
                               *p_actual, *p_previous;
1129 1130 1131 1132 1133 1134 1135

    if( !p_input )
        return NULL;

    vlc_value_t val_list, text_list;
    var_Change( p_input, psz_variable, VLC_VAR_GETLIST, &val_list, &text_list);

Rémi Duraffort's avatar
Rémi Duraffort committed
1136