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

Clément Stenac's avatar
Clément Stenac committed
30
#include <vlc/libvlc.h>
31 32 33
#include <vlc/libvlc_media.h>
#include <vlc/libvlc_events.h>

Clément Stenac's avatar
Clément Stenac committed
34 35
#include <vlc_demux.h>
#include <vlc_input.h>
36
#include <vlc_vout.h>
37
#include <vlc_keys.h>
38 39 40 41

#include "libvlc_internal.h"
#include "media_internal.h" // libvlc_media_set_state()
#include "media_player_internal.h"
Clément Stenac's avatar
Clément Stenac committed
42

43
/*
44
 * mapping of libvlc_navigate_mode_t to vlc_action_t
45
 */
46
static const vlc_action_t libvlc_navigate_to_action[] =
47
{
48 49 50 51 52
    ACTIONID_NAV_ACTIVATE,
    ACTIONID_NAV_UP,
    ACTIONID_NAV_DOWN,
    ACTIONID_NAV_LEFT,
    ACTIONID_NAV_RIGHT
53
};
54 55 56 57 58

static const uint32_t libvlc_navigate_to_action_size =			\
  sizeof( libvlc_navigate_to_action ) / sizeof( libvlc_navigate_to_action[0] );


59
static int
60 61 62 63 64 65 66 67
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
68
input_event_changed( vlc_object_t * p_this, char const * psz_cmd,
69 70 71
                     vlc_value_t oldval, vlc_value_t newval,
                     void * p_userdata );

72 73 74
static int
snapshot_was_taken( vlc_object_t *p_this, char const *psz_cmd,
                    vlc_value_t oldval, vlc_value_t newval, void *p_data );
75

76
static void libvlc_media_player_destroy( libvlc_media_player_t *p_mi );
77

78 79 80 81 82 83 84
/*
 * Shortcuts
 */

#define register_event(a, b) __register_event(a, libvlc_MediaPlayer ## b)
static inline void __register_event(libvlc_media_player_t *mp, libvlc_event_type_t type)
{
85
    libvlc_event_manager_register_event_type(mp->p_event_manager, type);
86 87
}

88 89 90 91 92 93 94 95 96
/*
 * The input lock protects the input and input resource pointer.
 * It MUST NOT be used from callbacks.
 *
 * The object lock protects the reset, namely the media and the player state.
 * It can, and usually needs to be taken from callbacks.
 * The object lock can be acquired under the input lock... and consequently
 * the opposite order is STRICTLY PROHIBITED.
 */
97 98 99 100 101 102 103 104 105 106
static inline void lock(libvlc_media_player_t *mp)
{
    vlc_mutex_lock(&mp->object_lock);
}

static inline void unlock(libvlc_media_player_t *mp)
{
    vlc_mutex_unlock(&mp->object_lock);
}

107 108 109 110 111 112 113 114 115 116
static inline void lock_input(libvlc_media_player_t *mp)
{
    vlc_mutex_lock(&mp->input.lock);
}

static inline void unlock_input(libvlc_media_player_t *mp)
{
    vlc_mutex_unlock(&mp->input.lock);
}

117
/*
JP Dinger's avatar
JP Dinger committed
118
 * Release the associated input thread.
119 120
 *
 * Object lock is NOT held.
121
 * Input lock is held or instance is being destroyed.
122
 */
123
static void release_input_thread( libvlc_media_player_t *p_mi, bool b_input_abort )
124
{
125
    assert( p_mi );
126

127
    input_thread_t *p_input_thread = p_mi->input.p_thread;
128
    if( !p_input_thread )
129
        return;
130
    p_mi->input.p_thread = NULL;
131

132 133 134 135 136 137 138 139 140
    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 );
141
    input_Close( p_input_thread );
142 143
}

144 145
/*
 * Retrieve the input thread. Be sure to release the object
146
 * once you are done with it. (libvlc Internal)
147
 */
148
input_thread_t *libvlc_get_input_thread( libvlc_media_player_t *p_mi )
Clément Stenac's avatar
Clément Stenac committed
149 150 151
{
    input_thread_t *p_input_thread;

152
    assert( p_mi );
153

154 155
    lock_input(p_mi);
    p_input_thread = p_mi->input.p_thread;
156 157
    if( p_input_thread )
        vlc_object_hold( p_input_thread );
158 159
    else
        libvlc_printerr( "No active input" );
160
    unlock_input(p_mi);
161

162 163 164
    return p_input_thread;
}

165 166 167 168 169
/*
 * Set the internal state of the media_player. (media player Internal)
 *
 * Function will lock the media_player.
 */
170 171 172 173 174
static void set_state( libvlc_media_player_t *p_mi, libvlc_state_t state,
    bool b_locked )
{
    if(!b_locked)
        lock(p_mi);
175
    p_mi->state = state;
176

177 178 179
    libvlc_media_t *media = p_mi->p_md;
    if (media)
        libvlc_media_retain(media);
180

181 182
    if(!b_locked)
        unlock(p_mi);
183

184 185
    if (media)
    {
186 187 188
        // Also set the state of the corresponding media
        // This is strictly for convenience.
        libvlc_media_set_state(media, state);
189 190

        libvlc_media_release(media);
191 192 193
    }
}

194
static int
195
input_seekable_changed( vlc_object_t * p_this, char const * psz_cmd,
196 197 198 199
                        vlc_value_t oldval, vlc_value_t newval,
                        void * p_userdata )
{
    VLC_UNUSED(oldval);
200 201
    VLC_UNUSED(p_this);
    VLC_UNUSED(psz_cmd);
202
    libvlc_media_player_t * p_mi = p_userdata;
203 204
    libvlc_event_t event;

205
    event.type = libvlc_MediaPlayerSeekableChanged;
206
    event.u.media_player_seekable_changed.new_seekable = newval.b_bool;
207 208 209 210 211 212 213 214 215 216 217

    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);
218 219
    VLC_UNUSED(p_this);
    VLC_UNUSED(psz_cmd);
220
    libvlc_media_player_t * p_mi = p_userdata;
221 222
    libvlc_event_t event;

223
    event.type = libvlc_MediaPlayerPausableChanged;
224
    event.u.media_player_pausable_changed.new_pausable = newval.b_bool;
225 226 227 228 229

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

230
static int
Laurent Aimar's avatar
Laurent Aimar committed
231
input_event_changed( vlc_object_t * p_this, char const * psz_cmd,
232 233 234
                     vlc_value_t oldval, vlc_value_t newval,
                     void * p_userdata )
{
235
    VLC_UNUSED(oldval);
Laurent Aimar's avatar
Laurent Aimar committed
236
    input_thread_t * p_input = (input_thread_t *)p_this;
237
    libvlc_media_player_t * p_mi = p_userdata;
Laurent Aimar's avatar
Laurent Aimar committed
238
    libvlc_event_t event;
239

240
    assert( !strcmp( psz_cmd, "intf-event" ) );
241

242 243
    if( newval.i_int == INPUT_EVENT_STATE )
    {
244
        libvlc_state_t libvlc_state;
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275

        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;
        }
276

277
        set_state( p_mi, libvlc_state, false );
278 279
        libvlc_event_send( p_mi->p_event_manager, &event );
    }
280 281 282 283 284
    else if( newval.i_int == INPUT_EVENT_ABORT )
    {
        libvlc_state_t libvlc_state = libvlc_Stopped;
        event.type = libvlc_MediaPlayerStopped;

285
        set_state( p_mi, libvlc_state, false );
286 287
        libvlc_event_send( p_mi->p_event_manager, &event );
    }
288
    else if( newval.i_int == INPUT_EVENT_POSITION )
289 290 291 292 293 294
    {
        if( var_GetInteger( p_input, "state" ) != PLAYING_S )
            return VLC_SUCCESS; /* Don't send the position while stopped */

        /* */
        event.type = libvlc_MediaPlayerPositionChanged;
JP Dinger's avatar
JP Dinger committed
295 296
        event.u.media_player_position_changed.new_position =
                                          var_GetFloat( p_input, "position" );
297 298 299 300
        libvlc_event_send( p_mi->p_event_manager, &event );

        /* */
        event.type = libvlc_MediaPlayerTimeChanged;
JP Dinger's avatar
JP Dinger committed
301
        event.u.media_player_time_changed.new_time =
302
           from_mtime(var_GetTime( p_input, "time" ));
303 304
        libvlc_event_send( p_mi->p_event_manager, &event );
    }
305 306 307 308
    else if( newval.i_int == INPUT_EVENT_LENGTH )
    {
        event.type = libvlc_MediaPlayerLengthChanged;
        event.u.media_player_length_changed.new_length =
309
           from_mtime(var_GetTime( p_input, "length" ));
310 311
        libvlc_event_send( p_mi->p_event_manager, &event );
    }
312 313 314 315 316 317 318
    else if( newval.i_int == INPUT_EVENT_CACHE )
    {
        event.type = libvlc_MediaPlayerBuffering;
        event.u.media_player_buffering.new_cache = (int)(100 *
            var_GetFloat( p_input, "cache" ));
        libvlc_event_send( p_mi->p_event_manager, &event );
    }
Laurent Aimar's avatar
Laurent Aimar committed
319

320 321 322
    return VLC_SUCCESS;
}

323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
/**************************************************************************
 * Snapshot Taken Event.
 *
 * FIXME: This snapshot API interface makes no sense in media_player.
 *************************************************************************/
static int snapshot_was_taken(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 *mp = p_data;
    libvlc_event_t event;
    event.type = libvlc_MediaPlayerSnapshotTaken;
    event.u.media_player_snapshot_taken.psz_filename = newval.psz_string;
    libvlc_event_send(mp->p_event_manager, &event);

    return VLC_SUCCESS;
}

342 343 344
static input_thread_t *find_input (vlc_object_t *obj)
{
    libvlc_media_player_t *mp = (libvlc_media_player_t *)obj;
345 346

    return libvlc_get_input_thread (mp);
347 348
}

349
/* */
350 351
static void libvlc_media_player_destroy( libvlc_media_player_t * );

352

353
/**************************************************************************
JP Dinger's avatar
JP Dinger committed
354 355 356 357 358 359 360 361 362 363 364 365
 * 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.
366
 **************************************************************************/
367
libvlc_media_player_t *
Rémi Denis-Courmont's avatar
good  
Rémi Denis-Courmont committed
368
libvlc_media_player_new( libvlc_instance_t *instance )
369
{
370
    libvlc_media_player_t * mp;
371

372
    assert(instance);
373

374 375
    mp = vlc_object_create (instance->p_libvlc_int, sizeof(*mp));
    if (unlikely(mp == NULL))
376
    {
377
        libvlc_printerr("Not enough memory");
378 379
        return NULL;
    }
380

381 382 383
    /* Input */
    var_Create (mp, "rate", VLC_VAR_FLOAT|VLC_VAR_DOINHERIT);

384
    /* Video */
385
    var_Create (mp, "vout", VLC_VAR_STRING|VLC_VAR_DOINHERIT);
386
    var_Create (mp, "window", VLC_VAR_STRING);
387 388 389 390
    var_Create (mp, "vmem-lock", VLC_VAR_ADDRESS);
    var_Create (mp, "vmem-unlock", VLC_VAR_ADDRESS);
    var_Create (mp, "vmem-display", VLC_VAR_ADDRESS);
    var_Create (mp, "vmem-data", VLC_VAR_ADDRESS);
391 392
    var_Create (mp, "vmem-setup", VLC_VAR_ADDRESS);
    var_Create (mp, "vmem-cleanup", VLC_VAR_ADDRESS);
393 394 395
    var_Create (mp, "vmem-chroma", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
    var_Create (mp, "vmem-width", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
    var_Create (mp, "vmem-height", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
396
    var_Create (mp, "vmem-pitch", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
397 398
    var_Create (mp, "drawable-xid", VLC_VAR_INTEGER);
#ifdef WIN32
399
    var_Create (mp, "drawable-hwnd", VLC_VAR_INTEGER);
400 401 402 403 404 405 406 407 408
#endif
#ifdef __APPLE__
    var_Create (mp, "drawable-agl", VLC_VAR_INTEGER);
    var_Create (mp, "drawable-nsobject", VLC_VAR_ADDRESS);
#endif

    var_Create (mp, "keyboard-events", VLC_VAR_BOOL);
    var_SetBool (mp, "keyboard-events", true);
    var_Create (mp, "mouse-events", VLC_VAR_BOOL);
409
    var_SetBool (mp, "mouse-events", true);
410

411 412 413 414 415 416 417 418 419
    var_Create (mp, "fullscreen", VLC_VAR_BOOL);
    var_Create (mp, "autoscale", VLC_VAR_BOOL);
    var_SetBool (mp, "autoscale", true);
    var_Create (mp, "scale", VLC_VAR_FLOAT);
    var_SetFloat (mp, "scale", 1.);
    var_Create (mp, "aspect-ratio", VLC_VAR_STRING);
    var_Create (mp, "crop", VLC_VAR_STRING);
    var_Create (mp, "deinterlace", VLC_VAR_INTEGER);
    var_Create (mp, "deinterlace-mode", VLC_VAR_STRING);
420

421
    var_Create (mp, "vbi-page", VLC_VAR_INTEGER);
422

423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440
    var_Create (mp, "marq-marquee", VLC_VAR_STRING);
    var_Create (mp, "marq-color", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
    var_Create (mp, "marq-opacity", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
    var_Create (mp, "marq-position", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
    var_Create (mp, "marq-refresh", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
    var_Create (mp, "marq-size", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
    var_Create (mp, "marq-timeout", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
    var_Create (mp, "marq-x", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
    var_Create (mp, "marq-y", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);

    var_Create (mp, "logo-file", VLC_VAR_STRING);
    var_Create (mp, "logo-x", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
    var_Create (mp, "logo-y", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
    var_Create (mp, "logo-delay", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
    var_Create (mp, "logo-repeat", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
    var_Create (mp, "logo-opacity", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
    var_Create (mp, "logo-position", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);

441 442 443 444 445 446
    var_Create (mp, "contrast", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT);
    var_Create (mp, "brightness", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT);
    var_Create (mp, "hue", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
    var_Create (mp, "saturation", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT);
    var_Create (mp, "gamma", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT);

447
     /* Audio */
448
    var_Create (mp, "aout", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
449
    var_Create (mp, "mute", VLC_VAR_BOOL);
450
    var_Create (mp, "volume", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
451 452
    var_Create (mp, "find-input-callback", VLC_VAR_ADDRESS);
    var_SetAddress (mp, "find-input-callback", find_input);
453 454 455 456
    var_Create (mp, "amem-data", VLC_VAR_ADDRESS);
    var_Create (mp, "amem-setup", VLC_VAR_ADDRESS);
    var_Create (mp, "amem-close", VLC_VAR_ADDRESS);
    var_Create (mp, "amem-play", VLC_VAR_ADDRESS);
457 458 459 460
    var_Create (mp, "amem-pause", VLC_VAR_ADDRESS);
    var_Create (mp, "amem-resume", VLC_VAR_ADDRESS);
    var_Create (mp, "amem-flush", VLC_VAR_ADDRESS);
    var_Create (mp, "amem-drain", VLC_VAR_ADDRESS);
461 462 463 464
    var_Create (mp, "amem-set-volume", VLC_VAR_ADDRESS);
    var_Create (mp, "amem-format", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
    var_Create (mp, "amem-rate", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
    var_Create (mp, "amem-channels", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
465

466
    mp->p_md = NULL;
467
    mp->state = libvlc_NothingSpecial;
468
    mp->p_libvlc_instance = instance;
469 470 471
    mp->input.p_thread = NULL;
    mp->input.p_resource = NULL;
    vlc_mutex_init (&mp->input.lock);
472
    mp->i_refcount = 1;
473 474
    mp->p_event_manager = libvlc_event_manager_new(mp, instance);
    if (unlikely(mp->p_event_manager == NULL))
475
    {
476
        vlc_object_release(mp);
477 478
        return NULL;
    }
479
    vlc_mutex_init(&mp->object_lock);
480

481 482 483 484 485 486 487 488 489 490
    register_event(mp, NothingSpecial);
    register_event(mp, Opening);
    register_event(mp, Buffering);
    register_event(mp, Playing);
    register_event(mp, Paused);
    register_event(mp, Stopped);
    register_event(mp, Forward);
    register_event(mp, Backward);
    register_event(mp, EndReached);
    register_event(mp, EncounteredError);
491
    register_event(mp, SeekableChanged);
492 493 494 495 496 497

    register_event(mp, PositionChanged);
    register_event(mp, TimeChanged);
    register_event(mp, LengthChanged);
    register_event(mp, TitleChanged);
    register_event(mp, PausableChanged);
498

499
    /* Snapshot initialization */
500
    register_event(mp, SnapshotTaken);
Jean-Paul Saman's avatar
Jean-Paul Saman committed
501

502 503
    register_event(mp, MediaChanged);

504
    /* Attach a var callback to the global object to provide the glue between
505 506 507 508 509 510
     * vout_thread that generates the event and media_player that re-emits it
     * with its own event manager
     *
     * FIXME: It's unclear why we want to put this in public API, and why we
     * want to expose it in such a limiting and ugly way.
     */
511
    var_AddCallback(mp->p_libvlc, "snapshot-file", snapshot_was_taken, mp);
512

513
    libvlc_retain(instance);
514
    return mp;
515 516 517
}

/**************************************************************************
JP Dinger's avatar
JP Dinger committed
518
 * Create a Media Instance object with a media descriptor.
519
 **************************************************************************/
520
libvlc_media_player_t *
Rémi Denis-Courmont's avatar
good  
Rémi Denis-Courmont committed
521
libvlc_media_player_new_from_media( libvlc_media_t * p_md )
522
{
523
    libvlc_media_player_t * p_mi;
524

Rémi Denis-Courmont's avatar
good  
Rémi Denis-Courmont committed
525
    p_mi = libvlc_media_player_new( p_md->p_libvlc_instance );
526
    if( !p_mi )
527 528
        return NULL;

529
    libvlc_media_retain( p_md );
530
    p_mi->p_md = p_md;
531 532 533 534 535

    return p_mi;
}

/**************************************************************************
536
 * Destroy a Media Instance object (libvlc internal)
537
 *
JP Dinger's avatar
JP Dinger committed
538
 * Warning: No lock held here, but hey, this is internal. Caller must lock.
539
 **************************************************************************/
540
static void libvlc_media_player_destroy( libvlc_media_player_t *p_mi )
541
{
542
    assert( p_mi );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
543

544
    /* Detach Callback from the main libvlc object */
545
    var_DelCallback( p_mi->p_libvlc,
546
                     "snapshot-file", snapshot_was_taken, p_mi );
547

548 549
    /* No need for lock_input() because no other threads knows us anymore */
    if( p_mi->input.p_thread )
550
        release_input_thread(p_mi, true);
551
    if( p_mi->input.p_resource )
552
    {
553 554
        input_resource_Terminate( p_mi->input.p_resource );
        input_resource_Release( p_mi->input.p_resource );
555
        p_mi->input.p_resource = NULL;
556
    }
557
    vlc_mutex_destroy( &p_mi->input.lock );
558

559
    libvlc_event_manager_release( p_mi->p_event_manager );
560
    libvlc_media_release( p_mi->p_md );
561
    vlc_mutex_destroy( &p_mi->object_lock );
562 563

    libvlc_instance_t *instance = p_mi->p_libvlc_instance;
564
    vlc_object_release( p_mi );
565
    libvlc_release(instance);
566 567 568
}

/**************************************************************************
JP Dinger's avatar
JP Dinger committed
569 570 571
 * Release a Media Instance object.
 *
 * Function does the locking.
572
 **************************************************************************/
573
void libvlc_media_player_release( libvlc_media_player_t *p_mi )
574
{
575
    bool destroy;
576

577
    assert( p_mi );
578
    lock(p_mi);
579
    destroy = !--p_mi->i_refcount;
580
    unlock(p_mi);
581

582 583
    if( destroy )
        libvlc_media_player_destroy( p_mi );
584
}
585

586
/**************************************************************************
JP Dinger's avatar
JP Dinger committed
587 588 589
 * Retain a Media Instance object.
 *
 * Caller must hold the lock.
590
 **************************************************************************/
591
void libvlc_media_player_retain( libvlc_media_player_t *p_mi )
592
{
593
    assert( p_mi );
594

595
    lock(p_mi);
596
    p_mi->i_refcount++;
597
    unlock(p_mi);
598
}
599

600
/**************************************************************************
JP Dinger's avatar
JP Dinger committed
601 602 603
 * Set the Media descriptor associated with the instance.
 *
 * Enter without lock -- function will lock the object.
604
 **************************************************************************/
605 606
void libvlc_media_player_set_media(
                            libvlc_media_player_t *p_mi,
607
                            libvlc_media_t *p_md )
608
{
609
    lock_input(p_mi);
610

611 612 613
    /* FIXME I am not sure if it is a user request or on die(eof/error)
     * request here */
    release_input_thread( p_mi,
614 615 616
                          p_mi->input.p_thread &&
                          !p_mi->input.p_thread->b_eof &&
                          !p_mi->input.p_thread->b_error );
617

618
    lock( p_mi );
619
    set_state( p_mi, libvlc_NothingSpecial, true );
620
    unlock_input( p_mi );
621

622
    libvlc_media_release( p_mi->p_md );
623

624 625 626
    if( !p_md )
    {
        p_mi->p_md = NULL;
627
        unlock(p_mi);
628 629 630
        return; /* It is ok to pass a NULL md */
    }

631
    libvlc_media_retain( p_md );
632
    p_mi->p_md = p_md;
633

634 635 636 637
    /* 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;

638
    unlock(p_mi);
639 640 641 642 643 644

    /* Send an event for the newly available media */
    libvlc_event_t event;
    event.type = libvlc_MediaPlayerMediaChanged;
    event.u.media_player_media_changed.new_media = p_md;
    libvlc_event_send( p_mi->p_event_manager, &event );
645

646 647 648
}

/**************************************************************************
JP Dinger's avatar
JP Dinger committed
649
 * Get the Media descriptor associated with the instance.
650
 **************************************************************************/
651
libvlc_media_t *
652
libvlc_media_player_get_media( libvlc_media_player_t *p_mi )
653
{
654
    libvlc_media_t *p_m;
655

656
    lock(p_mi);
657 658 659
    p_m = p_mi->p_md;
    if( p_m )
        libvlc_media_retain( p_mi->p_md );
660
    unlock(p_mi);
661
    return p_mi->p_md;
662 663
}

664
/**************************************************************************
JP Dinger's avatar
JP Dinger committed
665
 * Get the event Manager.
666 667
 **************************************************************************/
libvlc_event_manager_t *
668
libvlc_media_player_event_manager( libvlc_media_player_t *p_mi )
669 670 671 672
{
    return p_mi->p_event_manager;
}

673
/**************************************************************************
JP Dinger's avatar
JP Dinger committed
674
 * Tell media player to start playing.
675
 **************************************************************************/
Rémi Denis-Courmont's avatar
good  
Rémi Denis-Courmont committed
676
int libvlc_media_player_play( libvlc_media_player_t *p_mi )
677
{
678
    lock_input( p_mi );
679

680 681
    input_thread_t *p_input_thread = p_mi->input.p_thread;
    if( p_input_thread )
682
    {
Sam Hocevar's avatar
Sam Hocevar committed
683
        /* A thread already exists, send it a play message */
684
        input_Control( p_input_thread, INPUT_SET_STATE, PLAYING_S );
685
        unlock_input( p_mi );
Rémi Denis-Courmont's avatar
good  
Rémi Denis-Courmont committed
686
        return 0;
687 688
    }

689
    /* Ignore previous exception */
690
    lock(p_mi);
691

692 693
    if( !p_mi->p_md )
    {
694
        unlock(p_mi);
695
        unlock_input( p_mi );
696
        libvlc_printerr( "No associated media descriptor" );
Rémi Denis-Courmont's avatar
good  
Rémi Denis-Courmont committed
697
        return -1;
698 699
    }

700 701
    if( !p_mi->input.p_resource )
        p_mi->input.p_resource = input_resource_New( VLC_OBJECT( p_mi ) );
702 703 704 705
    p_input_thread = input_Create( p_mi, p_mi->p_md->p_input_item, NULL,
                                   p_mi->input.p_resource );
    unlock(p_mi);
    if( !p_input_thread )
706
    {
707
        unlock_input(p_mi);
Rémi Denis-Courmont's avatar
good  
Rémi Denis-Courmont committed
708 709
        libvlc_printerr( "Not enough memory" );
        return -1;
710
    }
711

712 713
    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
714
    var_AddCallback( p_input_thread, "intf-event", input_event_changed, p_mi );
715

716 717
    if( input_Start( p_input_thread ) )
    {
718 719 720 721
        unlock_input(p_mi);
        var_DelCallback( p_input_thread, "intf-event", input_event_changed, p_mi );
        var_DelCallback( p_input_thread, "can-pause", input_pausable_changed, p_mi );
        var_DelCallback( p_input_thread, "can-seek", input_seekable_changed, p_mi );
722
        vlc_object_release( p_input_thread );
723 724
        libvlc_printerr( "Input initialization failure" );
        return -1;
725
    }
726 727
    p_mi->input.p_thread = p_input_thread;
    unlock_input(p_mi);
Rémi Denis-Courmont's avatar
good  
Rémi Denis-Courmont committed
728
    return 0;
729 730
}

731
void libvlc_media_player_set_pause( libvlc_media_player_t *p_mi, int paused )
732
{
733
    input_thread_t * p_input_thread = libvlc_get_input_thread( p_mi );
734
    if( !p_input_thread )
735 736
        return;

737
    libvlc_state_t state = libvlc_media_player_get_state( p_mi );
738
    if( state == libvlc_Playing || state == libvlc_Buffering )
739
    {
740 741 742 743 744 745 746
        if( paused )
        {
            if( libvlc_media_player_can_pause( p_mi ) )
                input_Control( p_input_thread, INPUT_SET_STATE, PAUSE_S );
            else
                libvlc_media_player_stop( p_mi );
        }
747
    }
748
    else
749 750 751 752
    {
        if( !paused )
            input_Control( p_input_thread, INPUT_SET_STATE, PLAYING_S );
    }
753

754
    vlc_object_release( p_input_thread );
755
}
756

757 758 759 760 761 762 763 764 765 766 767
/**************************************************************************
 * Toggle pause.
 **************************************************************************/
void libvlc_media_player_pause( libvlc_media_player_t *p_mi )
{
    libvlc_state_t state = libvlc_media_player_get_state( p_mi );
    bool playing = (state == libvlc_Playing || state == libvlc_Buffering);

    libvlc_media_player_set_pause( p_mi, playing );
}

768
/**************************************************************************
JP Dinger's avatar
JP Dinger committed
769 770 771
 * Tells whether the media player is currently playing.
 *
 * Enter with lock held.
772
 **************************************************************************/
773
int libvlc_media_player_is_playing( libvlc_media_player_t *p_mi )
774
{
775
    libvlc_state_t state = libvlc_media_player_get_state( p_mi );
776
    return (libvlc_Playing == state) || (libvlc_Buffering == state);
777 778
}

779
/**************************************************************************
JP Dinger's avatar
JP Dinger committed
780
 * Stop playing.
781
 **************************************************************************/
782
void libvlc_media_player_stop( libvlc_media_player_t *p_mi )
783
{
784
    libvlc_state_t state = libvlc_media_player_get_state( p_mi );
785

786
    lock_input(p_mi);
787 788 789 790 791
    release_input_thread( p_mi, true ); /* This will stop the input thread */

    /* Force to go to stopped state, in case we were in Ended, or Error
     * state. */
    if( state != libvlc_Stopped )
792
    {
793
        set_state( p_mi, libvlc_Stopped, false );
794 795 796

        /* Construct and send the event */
        libvlc_event_t event;
797
        event.type = libvlc_MediaPlayerStopped;
798 799
        libvlc_event_send( p_mi->p_event_manager, &event );
    }
800 801

    if( p_mi->input.p_resource != NULL )
802
        input_resource_Terminate( p_mi->input.p_resource );
803
    unlock_input(p_mi);
804 805
}

806 807 808 809 810 811 812 813 814 815 816 817 818 819

void libvlc_video_set_callbacks( libvlc_media_player_t *mp,
    void *(*lock_cb) (void *, void **),
    void (*unlock_cb) (void *, void *, void *const *),
    void (*display_cb) (void *, void *),
    void *opaque )
{
    var_SetAddress( mp, "vmem-lock", lock_cb );
    var_SetAddress( mp, "vmem-unlock", unlock_cb );
    var_SetAddress( mp, "vmem-display", display_cb );
    var_SetAddress( mp, "vmem-data", opaque );
    var_SetString( mp, "vout", "vmem" );
}

820 821 822 823 824 825 826 827
void libvlc_video_set_format_callbacks( libvlc_media_player_t *mp,
                                        libvlc_video_format_cb setup,
                                        libvlc_video_cleanup_cb cleanup )
{
    var_SetAddress( mp, "vmem-setup", setup );
    var_SetAddress( mp, "vmem-cleanup", cleanup );
}

828 829 830 831 832 833 834 835 836
void libvlc_video_set_format( libvlc_media_player_t *mp, const char *chroma,
                              unsigned width, unsigned height, unsigned pitch )
{
    var_SetString( mp, "vmem-chroma", chroma );
    var_SetInteger( mp, "vmem-width", width );
    var_SetInteger( mp, "vmem-height", height );
    var_SetInteger( mp, "vmem-pitch", pitch );
}

837 838 839 840
/**************************************************************************
 * set_nsobject
 **************************************************************************/
void libvlc_media_player_set_nsobject( libvlc_media_player_t *p_mi,
841
                                        void * drawable )
842
{
843 844 845 846 847 848
    assert (p_mi != NULL);
#ifdef __APPLE__
    var_SetAddress (p_mi, "drawable-nsobject", drawable);
#else
    (void) p_mi; (void)drawable;
#endif
849 850 851
}

/**************************************************************************
852
 * get_nsobject
853
 **************************************************************************/
Pierre's avatar
Pierre committed
854
void * libvlc_media_player_get_nsobject( libvlc_media_player_t *p_mi )
855
{
856 857 858 859 860 861
    assert (p_mi != NULL);
#ifdef __APPLE__
    return var_GetAddress (p_mi, "drawable-nsobject");
#else
    return NULL;
#endif
862 863
}

864 865 866 867
/**************************************************************************
 * set_agl
 **************************************************************************/
void libvlc_media_player_set_agl( libvlc_media_player_t *p_mi,
868
                                  uint32_t drawable )
869
{
870 871 872 873 874
#ifdef __APPLE__
    var_SetInteger (p_mi, "drawable-agl", drawable);
#else
    (void) p_mi; (void)drawable;
#endif
875 876 877 878 879 880 881
}

/**************************************************************************
 * get_agl
 **************************************************************************/
uint32_t libvlc_media_player_get_agl( libvlc_media_player_t *p_mi )
{
882 883 884 885 886 887
    assert (p_mi != NULL);
#ifdef __APPLE__
    return var_GetInteger (p_mi, "drawable-agl");
#else
    return 0;
#endif
888 889
}

JP Dinger's avatar
JP Dinger committed
890 891 892
/**************************************************************************
 * set_xwindow
 **************************************************************************/
893
void libvlc_media_player_set_xwindow( libvlc_media_player_t *p_mi,
894
                                      uint32_t drawable )
895
{
896
    assert (p_mi != NULL);
897 898

    var_SetString (p_mi, "vout", drawable ? "xid" : "any");
899
    var_SetString (p_mi, "window", drawable ? "embed-xid,any" : "any");
900
    var_SetInteger (p_mi, "drawable-xid", drawable);
901 902
}

JP Dinger's avatar
JP Dinger committed
903 904 905
/**************************************************************************
 * get_xwindow
 **************************************************************************/
906 907
uint32_t libvlc_media_player_get_xwindow( libvlc_media_player_t *p_mi )
{
908
    return var_GetInteger (p_mi, "drawable-xid");
909 910
}

JP Dinger's avatar
JP Dinger committed
911 912 913
/**************************************************************************
 * set_hwnd
 **************************************************************************/
914
void libvlc_media_player_set_hwnd( libvlc_media_player_t *p_mi,
915
                                   void *drawable )
916
{
917 918
    assert (p_mi != NULL);
#ifdef WIN32
919 920
    var_SetString (p_mi, "window",
                   (drawable != NULL) ? "embed-hwnd,any" : "");
921
    var_SetInteger (p_mi, "drawable-hwnd", (uintptr_t)drawable);
922 923 924
#else
    (void) p_mi; (void) drawable;
#endif
925 926
}

JP Dinger's avatar
JP Dinger committed
927 928 929
/**************************************************************************
 * get_hwnd
 **************************************************************************/
930 931
void *libvlc_media_player_get_hwnd( libvlc_media_player_t *p_mi )
{
932 933
    assert (p_mi != NULL);
#ifdef WIN32
934
    return (void *)(uintptr_t)var_GetInteger (p_mi, "drawable-hwnd");
935 936 937
#else
    return NULL;
#endif
938 939
}

940 941
void libvlc_audio_set_callbacks( libvlc_media_player_t *mp,
                                 libvlc_audio_play_cb play_cb,
942 943 944 945
                                 libvlc_audio_pause_cb pause_cb,
                                 libvlc_audio_resume_cb resume_cb,
                                 libvlc_audio_flush_cb flush_cb,
                                 libvlc_audio_drain_cb drain_cb,
946 947 948
                                 void *opaque )
{
    var_SetAddress( mp, "amem-play", play_cb );
949 950 951 952
    var_SetAddress( mp, "amem-pause", pause_cb );
    var_SetAddress( mp, "amem-resume", resume_cb );
    var_SetAddress( mp, "amem-flush", flush_cb );
    var_SetAddress( mp, "amem-drain", drain_cb );
953
    var_SetAddress( mp, "amem-data", opaque );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
954
    var_SetString( mp, "aout", "amem,none" );
955 956
}

957 958 959 960 961 962
void libvlc_audio_set_volume_callback( libvlc_media_player_t *mp,
                                       libvlc_audio_set_volume_cb cb )
{
    var_SetAddress( mp, "amem-set-volume", cb );
}

963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979
void libvlc_audio_set_format_callbacks( libvlc_media_player_t *mp,
                                        libvlc_audio_setup_cb setup,
                                        libvlc_audio_cleanup_cb cleanup )
{
    var_SetAddress( mp, "amem-setup", setup );
    var_SetAddress( mp, "amem-cleanup", cleanup );
}

void libvlc_audio_set_format( libvlc_media_player_t *mp, const char *format,
                              unsigned rate, unsigned channels )
{
    var_SetString( mp, "amem-format", format );
    var_SetInteger( mp, "amem-rate", rate );
    var_SetInteger( mp, "amem-channels", channels );
}


980 981 982
/**************************************************************************
 * Getters for stream information
 **************************************************************************/
983
libvlc_time_t libvlc_media_player_get_length(
Rémi Denis-Courmont's avatar
good  
Rémi Denis-Courmont committed
984
                             libvlc_media_player_t *p_mi )
985 986
{
    input_thread_t *p_input_thread;
987
    libvlc_time_t i_time;
988

989
    p_input_thread = libvlc_get_input_thread ( p_mi );
990
    if( !p_input_thread )
Jean-Paul Saman's avatar
Jean-Paul Saman committed
991 992
        return -1;

993
    i_time = from_mtime(var_GetTime( p_input_thread, "length" ));
Clément Stenac's avatar
Clément Stenac committed
994 995
    vlc_object_release( p_input_thread );

996
    return i_time;
Clément Stenac's avatar
Clément Stenac committed
997 998
}

Rémi Denis-Courmont's avatar
good  
Rémi Denis-Courmont committed
999
libvlc_time_t libvlc_media_player_get_time( libvlc_media_player_t *p_mi )
Clément Stenac's avatar
Clément Stenac committed
1000 1001
{
    input_thread_t *p_input_thread;
1002
    libvlc_time_t i_time;