media_player.c 44 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
 *****************************************************************************
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
4
 * Copyright (C) 2005-2011 VLC authors and VideoLAN
Clément Stenac's avatar
Clément Stenac committed
5
 *
6
 * Authors: Clément Stenac <zorglub@videolan.org>
Clément Stenac's avatar
Clément Stenac committed
7
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
8 9 10
 * 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
Clément Stenac's avatar
Clément Stenac committed
11 12 13 14
 * (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
15 16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
Clément Stenac's avatar
Clément Stenac committed
17
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
18 19 20
 * 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.
Clément Stenac's avatar
Clément Stenac committed
21 22
 *****************************************************************************/

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

27
#include <assert.h>
28

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

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

#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
41

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

54
static const uint32_t libvlc_navigate_to_action_size =                        \
55 56 57
  sizeof( libvlc_navigate_to_action ) / sizeof( libvlc_navigate_to_action[0] );


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

71 72 73
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 );
74

75
static void libvlc_media_player_destroy( libvlc_media_player_t *p_mi );
76

77 78 79 80 81 82 83
/*
 * 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)
{
84
    libvlc_event_manager_register_event_type(mp->p_event_manager, type);
85 86
}

87 88 89 90 91 92 93 94 95
/*
 * 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.
 */
96 97 98 99 100 101 102 103 104 105
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);
}

106 107 108 109 110 111 112 113 114 115
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);
}

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

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

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

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

151
    assert( p_mi );
152

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

161 162 163
    return p_input_thread;
}

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

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

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

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

        libvlc_media_release(media);
190 191 192
    }
}

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

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

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

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

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

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

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

241 242
    if( newval.i_int == INPUT_EVENT_STATE )
    {
243
        libvlc_state_t libvlc_state;
244 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

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

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

284
        set_state( p_mi, libvlc_state, false );
285 286
        libvlc_event_send( p_mi->p_event_manager, &event );
    }
287
    else if( newval.i_int == INPUT_EVENT_POSITION )
288 289 290 291 292 293
    {
        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
294 295
        event.u.media_player_position_changed.new_position =
                                          var_GetFloat( p_input, "position" );
296 297 298 299
        libvlc_event_send( p_mi->p_event_manager, &event );

        /* */
        event.type = libvlc_MediaPlayerTimeChanged;
JP Dinger's avatar
JP Dinger committed
300
        event.u.media_player_time_changed.new_time =
301
           from_mtime(var_GetTime( p_input, "time" ));
302 303
        libvlc_event_send( p_mi->p_event_manager, &event );
    }
304 305 306 307
    else if( newval.i_int == INPUT_EVENT_LENGTH )
    {
        event.type = libvlc_MediaPlayerLengthChanged;
        event.u.media_player_length_changed.new_length =
308
           from_mtime(var_GetTime( p_input, "length" ));
309 310
        libvlc_event_send( p_mi->p_event_manager, &event );
    }
311 312 313 314 315 316 317
    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 );
    }
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336
    else if( newval.i_int == INPUT_EVENT_VOUT )
    {
        vout_thread_t **pp_vout;
        size_t i_vout;
        if( input_Control( p_input, INPUT_GET_VOUTS, &pp_vout, &i_vout ) )
        {
            i_vout  = 0;
        }
        else
        {
            for( size_t i = 0; i < i_vout; i++ )
                vlc_object_release( pp_vout[i] );
            free( pp_vout );
        }

        event.type = libvlc_MediaPlayerVout;
        event.u.media_player_vout.new_count = i_vout;
        libvlc_event_send( p_mi->p_event_manager, &event );
    }
Laurent Aimar's avatar
Laurent Aimar committed
337

338 339 340
    return VLC_SUCCESS;
}

341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
/**************************************************************************
 * 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;
}

360 361 362
static input_thread_t *find_input (vlc_object_t *obj)
{
    libvlc_media_player_t *mp = (libvlc_media_player_t *)obj;
363 364

    return libvlc_get_input_thread (mp);
365 366
}

367
/* */
368 369
static void libvlc_media_player_destroy( libvlc_media_player_t * );

370

371
/**************************************************************************
JP Dinger's avatar
JP Dinger committed
372 373 374 375 376 377 378 379 380 381 382 383
 * 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.
384
 **************************************************************************/
385
libvlc_media_player_t *
Rémi Denis-Courmont's avatar
good  
Rémi Denis-Courmont committed
386
libvlc_media_player_new( libvlc_instance_t *instance )
387
{
388
    libvlc_media_player_t * mp;
389

390
    assert(instance);
391

392 393
    mp = vlc_object_create (instance->p_libvlc_int, sizeof(*mp));
    if (unlikely(mp == NULL))
394
    {
395
        libvlc_printerr("Not enough memory");
396 397
        return NULL;
    }
398

399 400 401
    /* Input */
    var_Create (mp, "rate", VLC_VAR_FLOAT|VLC_VAR_DOINHERIT);

402
    /* Video */
403
    var_Create (mp, "vout", VLC_VAR_STRING|VLC_VAR_DOINHERIT);
404
    var_Create (mp, "window", VLC_VAR_STRING);
405 406 407 408
    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);
409 410
    var_Create (mp, "vmem-setup", VLC_VAR_ADDRESS);
    var_Create (mp, "vmem-cleanup", VLC_VAR_ADDRESS);
411 412 413
    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);
414
    var_Create (mp, "vmem-pitch", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
415
    var_Create (mp, "drawable-xid", VLC_VAR_INTEGER);
416
#if defined (WIN32) || defined (__OS2__)
417
    var_Create (mp, "drawable-hwnd", VLC_VAR_INTEGER);
418 419 420 421 422 423 424 425 426
#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);
427
    var_SetBool (mp, "mouse-events", true);
428

429 430 431 432 433 434 435 436 437
    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);
438

439
    var_Create (mp, "vbi-page", VLC_VAR_INTEGER);
440

441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458
    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);

459 460 461 462 463 464
    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);

465
     /* Audio */
466
    var_Create (mp, "aout", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
467
    var_Create (mp, "mute", VLC_VAR_BOOL);
468
    var_Create (mp, "volume", VLC_VAR_FLOAT);
469 470
    var_Create (mp, "find-input-callback", VLC_VAR_ADDRESS);
    var_SetAddress (mp, "find-input-callback", find_input);
471
    var_Create (mp, "corks", VLC_VAR_INTEGER);
472 473
    var_Create (mp, "amem-data", VLC_VAR_ADDRESS);
    var_Create (mp, "amem-setup", VLC_VAR_ADDRESS);
Sébastien Toque's avatar
Sébastien Toque committed
474
    var_Create (mp, "amem-cleanup", VLC_VAR_ADDRESS);
475
    var_Create (mp, "amem-play", VLC_VAR_ADDRESS);
476 477 478 479
    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);
480 481 482 483
    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);
484

485
    mp->p_md = NULL;
486
    mp->state = libvlc_NothingSpecial;
487
    mp->p_libvlc_instance = instance;
488 489 490
    mp->input.p_thread = NULL;
    mp->input.p_resource = NULL;
    vlc_mutex_init (&mp->input.lock);
491
    mp->i_refcount = 1;
492 493
    mp->p_event_manager = libvlc_event_manager_new(mp, instance);
    if (unlikely(mp->p_event_manager == NULL))
494
    {
495
        vlc_object_release(mp);
496 497
        return NULL;
    }
498
    vlc_mutex_init(&mp->object_lock);
499

500 501 502 503 504 505 506 507 508 509
    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);
510
    register_event(mp, SeekableChanged);
511 512 513 514 515 516

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

518 519
    register_event(mp, Vout);

520
    /* Snapshot initialization */
521
    register_event(mp, SnapshotTaken);
Jean-Paul Saman's avatar
Jean-Paul Saman committed
522

523 524
    register_event(mp, MediaChanged);

525
    /* Attach a var callback to the global object to provide the glue between
526 527 528 529 530 531
     * 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.
     */
532
    var_AddCallback(mp->p_libvlc, "snapshot-file", snapshot_was_taken, mp);
533

534
    libvlc_retain(instance);
535
    return mp;
536 537 538
}

/**************************************************************************
JP Dinger's avatar
JP Dinger committed
539
 * Create a Media Instance object with a media descriptor.
540
 **************************************************************************/
541
libvlc_media_player_t *
Rémi Denis-Courmont's avatar
good  
Rémi Denis-Courmont committed
542
libvlc_media_player_new_from_media( libvlc_media_t * p_md )
543
{
544
    libvlc_media_player_t * p_mi;
545

Rémi Denis-Courmont's avatar
good  
Rémi Denis-Courmont committed
546
    p_mi = libvlc_media_player_new( p_md->p_libvlc_instance );
547
    if( !p_mi )
548 549
        return NULL;

550
    libvlc_media_retain( p_md );
551
    p_mi->p_md = p_md;
552 553 554 555 556

    return p_mi;
}

/**************************************************************************
557
 * Destroy a Media Instance object (libvlc internal)
558
 *
JP Dinger's avatar
JP Dinger committed
559
 * Warning: No lock held here, but hey, this is internal. Caller must lock.
560
 **************************************************************************/
561
static void libvlc_media_player_destroy( libvlc_media_player_t *p_mi )
562
{
563
    assert( p_mi );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
564

565
    /* Detach Callback from the main libvlc object */
566
    var_DelCallback( p_mi->p_libvlc,
567
                     "snapshot-file", snapshot_was_taken, p_mi );
568

569 570
    /* No need for lock_input() because no other threads knows us anymore */
    if( p_mi->input.p_thread )
571
        release_input_thread(p_mi, true);
572
    if( p_mi->input.p_resource )
573
    {
574 575
        input_resource_Terminate( p_mi->input.p_resource );
        input_resource_Release( p_mi->input.p_resource );
576
        p_mi->input.p_resource = NULL;
577
    }
578
    vlc_mutex_destroy( &p_mi->input.lock );
579

580
    libvlc_event_manager_release( p_mi->p_event_manager );
581
    libvlc_media_release( p_mi->p_md );
582
    vlc_mutex_destroy( &p_mi->object_lock );
583 584

    libvlc_instance_t *instance = p_mi->p_libvlc_instance;
585
    vlc_object_release( p_mi );
586
    libvlc_release(instance);
587 588 589
}

/**************************************************************************
JP Dinger's avatar
JP Dinger committed
590 591 592
 * Release a Media Instance object.
 *
 * Function does the locking.
593
 **************************************************************************/
594
void libvlc_media_player_release( libvlc_media_player_t *p_mi )
595
{
596
    bool destroy;
597

598
    assert( p_mi );
599
    lock(p_mi);
600
    destroy = !--p_mi->i_refcount;
601
    unlock(p_mi);
602

603 604
    if( destroy )
        libvlc_media_player_destroy( p_mi );
605
}
606

607
/**************************************************************************
JP Dinger's avatar
JP Dinger committed
608 609 610
 * Retain a Media Instance object.
 *
 * Caller must hold the lock.
611
 **************************************************************************/
612
void libvlc_media_player_retain( libvlc_media_player_t *p_mi )
613
{
614
    assert( p_mi );
615

616
    lock(p_mi);
617
    p_mi->i_refcount++;
618
    unlock(p_mi);
619
}
620

621
/**************************************************************************
JP Dinger's avatar
JP Dinger committed
622 623 624
 * Set the Media descriptor associated with the instance.
 *
 * Enter without lock -- function will lock the object.
625
 **************************************************************************/
626 627
void libvlc_media_player_set_media(
                            libvlc_media_player_t *p_mi,
628
                            libvlc_media_t *p_md )
629
{
630
    lock_input(p_mi);
631

632 633 634
    /* FIXME I am not sure if it is a user request or on die(eof/error)
     * request here */
    release_input_thread( p_mi,
635 636 637
                          p_mi->input.p_thread &&
                          !p_mi->input.p_thread->b_eof &&
                          !p_mi->input.p_thread->b_error );
638

639
    lock( p_mi );
640
    set_state( p_mi, libvlc_NothingSpecial, true );
641
    unlock_input( p_mi );
642

643
    libvlc_media_release( p_mi->p_md );
644

645 646 647
    if( !p_md )
    {
        p_mi->p_md = NULL;
648
        unlock(p_mi);
649 650 651
        return; /* It is ok to pass a NULL md */
    }

652
    libvlc_media_retain( p_md );
653
    p_mi->p_md = p_md;
654

655 656 657 658
    /* 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;

659
    unlock(p_mi);
660 661 662 663 664 665

    /* 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 );
666

667 668 669
}

/**************************************************************************
JP Dinger's avatar
JP Dinger committed
670
 * Get the Media descriptor associated with the instance.
671
 **************************************************************************/
672
libvlc_media_t *
673
libvlc_media_player_get_media( libvlc_media_player_t *p_mi )
674
{
675
    libvlc_media_t *p_m;
676

677
    lock(p_mi);
678 679 680
    p_m = p_mi->p_md;
    if( p_m )
        libvlc_media_retain( p_mi->p_md );
681
    unlock(p_mi);
682
    return p_mi->p_md;
683 684
}

685
/**************************************************************************
JP Dinger's avatar
JP Dinger committed
686
 * Get the event Manager.
687 688
 **************************************************************************/
libvlc_event_manager_t *
689
libvlc_media_player_event_manager( libvlc_media_player_t *p_mi )
690 691 692 693
{
    return p_mi->p_event_manager;
}

694
/**************************************************************************
JP Dinger's avatar
JP Dinger committed
695
 * Tell media player to start playing.
696
 **************************************************************************/
Rémi Denis-Courmont's avatar
good  
Rémi Denis-Courmont committed
697
int libvlc_media_player_play( libvlc_media_player_t *p_mi )
698
{
699
    lock_input( p_mi );
700

701 702
    input_thread_t *p_input_thread = p_mi->input.p_thread;
    if( p_input_thread )
703
    {
Sam Hocevar's avatar
Sam Hocevar committed
704
        /* A thread already exists, send it a play message */
705
        input_Control( p_input_thread, INPUT_SET_STATE, PLAYING_S );
706
        unlock_input( p_mi );
Rémi Denis-Courmont's avatar
good  
Rémi Denis-Courmont committed
707
        return 0;
708 709
    }

710
    /* Ignore previous exception */
711
    lock(p_mi);
712

713 714
    if( !p_mi->p_md )
    {
715
        unlock(p_mi);
716
        unlock_input( p_mi );
717
        libvlc_printerr( "No associated media descriptor" );
Rémi Denis-Courmont's avatar
good  
Rémi Denis-Courmont committed
718
        return -1;
719 720
    }

721 722
    if( !p_mi->input.p_resource )
        p_mi->input.p_resource = input_resource_New( VLC_OBJECT( p_mi ) );
723 724 725 726
    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 )
727
    {
728
        unlock_input(p_mi);
Rémi Denis-Courmont's avatar
good  
Rémi Denis-Courmont committed
729 730
        libvlc_printerr( "Not enough memory" );
        return -1;
731
    }
732

733 734
    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
735
    var_AddCallback( p_input_thread, "intf-event", input_event_changed, p_mi );
736

737 738
    if( input_Start( p_input_thread ) )
    {
739 740 741 742
        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 );
743
        vlc_object_release( p_input_thread );
744 745
        libvlc_printerr( "Input initialization failure" );
        return -1;
746
    }
747 748
    p_mi->input.p_thread = p_input_thread;
    unlock_input(p_mi);
Rémi Denis-Courmont's avatar
good  
Rémi Denis-Courmont committed
749
    return 0;
750 751
}

752
void libvlc_media_player_set_pause( libvlc_media_player_t *p_mi, int paused )
753
{
754
    input_thread_t * p_input_thread = libvlc_get_input_thread( p_mi );
755
    if( !p_input_thread )
756 757
        return;

758
    libvlc_state_t state = libvlc_media_player_get_state( p_mi );
759
    if( state == libvlc_Playing || state == libvlc_Buffering )
760
    {
761 762 763 764 765 766 767
        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 );
        }
768
    }
769
    else
770 771 772 773
    {
        if( !paused )
            input_Control( p_input_thread, INPUT_SET_STATE, PLAYING_S );
    }
774

775
    vlc_object_release( p_input_thread );
776
}
777

778 779 780 781 782 783 784 785 786 787 788
/**************************************************************************
 * 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 );
}

789
/**************************************************************************
JP Dinger's avatar
JP Dinger committed
790 791 792
 * Tells whether the media player is currently playing.
 *
 * Enter with lock held.
793
 **************************************************************************/
794
int libvlc_media_player_is_playing( libvlc_media_player_t *p_mi )
795
{
796
    libvlc_state_t state = libvlc_media_player_get_state( p_mi );
797
    return (libvlc_Playing == state) || (libvlc_Buffering == state);
798 799
}

800
/**************************************************************************
JP Dinger's avatar
JP Dinger committed
801
 * Stop playing.
802
 **************************************************************************/
803
void libvlc_media_player_stop( libvlc_media_player_t *p_mi )
804
{
805
    libvlc_state_t state = libvlc_media_player_get_state( p_mi );
806

807
    lock_input(p_mi);
808 809 810 811 812
    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 )
813
    {
814
        set_state( p_mi, libvlc_Stopped, false );
815 816 817

        /* Construct and send the event */
        libvlc_event_t event;
818
        event.type = libvlc_MediaPlayerStopped;
819 820
        libvlc_event_send( p_mi->p_event_manager, &event );
    }
821 822

    if( p_mi->input.p_resource != NULL )
823
        input_resource_Terminate( p_mi->input.p_resource );
824
    unlock_input(p_mi);
825 826
}

827 828 829 830 831 832 833 834 835 836 837 838 839 840

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" );
}

841 842 843 844 845 846 847 848
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 );
}

849 850 851 852 853 854 855 856 857
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 );
}

858 859 860 861
/**************************************************************************
 * set_nsobject
 **************************************************************************/
void libvlc_media_player_set_nsobject( libvlc_media_player_t *p_mi,
862
                                        void * drawable )
863
{
864 865 866 867 868 869
    assert (p_mi != NULL);
#ifdef __APPLE__
    var_SetAddress (p_mi, "drawable-nsobject", drawable);
#else
    (void) p_mi; (void)drawable;
#endif
870 871 872
}

/**************************************************************************
873
 * get_nsobject
874
 **************************************************************************/
Pierre's avatar
Pierre committed
875
void * libvlc_media_player_get_nsobject( libvlc_media_player_t *p_mi )
876
{
877 878 879 880 881 882
    assert (p_mi != NULL);
#ifdef __APPLE__
    return var_GetAddress (p_mi, "drawable-nsobject");
#else
    return NULL;
#endif
883 884
}

885 886 887 888
/**************************************************************************
 * set_agl
 **************************************************************************/
void libvlc_media_player_set_agl( libvlc_media_player_t *p_mi,
889
                                  uint32_t drawable )
890
{
891 892 893 894 895
#ifdef __APPLE__
    var_SetInteger (p_mi, "drawable-agl", drawable);
#else
    (void) p_mi; (void)drawable;
#endif
896 897 898 899 900 901 902
}

/**************************************************************************
 * get_agl
 **************************************************************************/
uint32_t libvlc_media_player_get_agl( libvlc_media_player_t *p_mi )
{
903 904 905 906 907 908
    assert (p_mi != NULL);
#ifdef __APPLE__
    return var_GetInteger (p_mi, "drawable-agl");
#else
    return 0;
#endif
909 910
}

JP Dinger's avatar
JP Dinger committed
911 912 913
/**************************************************************************
 * set_xwindow
 **************************************************************************/
914
void libvlc_media_player_set_xwindow( libvlc_media_player_t *p_mi,
915
                                      uint32_t drawable )
916
{
917
    assert (p_mi != NULL);
918 919

    var_SetString (p_mi, "vout", drawable ? "xid" : "any");
920
    var_SetString (p_mi, "window", drawable ? "embed-xid,any" : "any");
921
    var_SetInteger (p_mi, "drawable-xid", drawable);
922 923
}

JP Dinger's avatar
JP Dinger committed
924 925 926
/**************************************************************************
 * get_xwindow
 **************************************************************************/
927 928
uint32_t libvlc_media_player_get_xwindow( libvlc_media_player_t *p_mi )
{
929
    return var_GetInteger (p_mi, "drawable-xid");
930 931
}

JP Dinger's avatar
JP Dinger committed
932 933 934
/**************************************************************************
 * set_hwnd
 **************************************************************************/
935
void libvlc_media_player_set_hwnd( libvlc_media_player_t *p_mi,
936
                                   void *drawable )
937
{
938
    assert (p_mi != NULL);
939
#if defined (WIN32) || defined (__OS2__)
940 941
    var_SetString (p_mi, "window",
                   (drawable != NULL) ? "embed-hwnd,any" : "");
942
    var_SetInteger (p_mi, "drawable-hwnd", (uintptr_t)drawable);
943 944 945
#else
    (void) p_mi; (void) drawable;
#endif
946 947
}

JP Dinger's avatar
JP Dinger committed
948 949 950
/**************************************************************************
 * get_hwnd
 **************************************************************************/
951 952
void *libvlc_media_player_get_hwnd( libvlc_media_player_t *p_mi )
{
953
    assert (p_mi != NULL);
954
#if defined (WIN32) || defined (__OS2__)
955
    return (void *)(uintptr_t)var_GetInteger (p_mi, "drawable-hwnd");
956 957 958
#else
    return NULL;
#endif
959 960
}

961 962
void libvlc_audio_set_callbacks( libvlc_media_player_t *mp,
                                 libvlc_audio_play_cb play_cb,
963 964 965 966
                                 libvlc_audio_pause_cb pause_cb,
                                 libvlc_audio_resume_cb resume_cb,
                                 libvlc_audio_flush_cb flush_cb,
                                 libvlc_audio_drain_cb drain_cb,
967 968 969
                                 void *opaque )
{
    var_SetAddress( mp, "amem-play", play_cb );
970 971 972 973
    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 );
974
    var_SetAddress( mp, "amem-data", opaque );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
975
    var_SetString( mp, "aout", "amem,none" );
976 977
}

978 979 980 981 982 983
void libvlc_audio_set_volume_callback( libvlc_media_player_t *mp,
                                       libvlc_audio_set_volume_cb cb )
{
    var_SetAddress( mp, "amem-set-volume", cb );
}

984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000
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 );
}


1001 1002 1003
/**************************************************************************
 * Getters for stream information
 **************************************************************************/
1004
libvlc_time_t libvlc_media_player_get_length(