media_player.c 43.9 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_INTEGER);
469 470
    var_Create (mp, "find-input-callback", VLC_VAR_ADDRESS);
    var_SetAddress (mp, "find-input-callback", find_input);
471 472
    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
473
    var_Create (mp, "amem-cleanup", VLC_VAR_ADDRESS);
474
    var_Create (mp, "amem-play", VLC_VAR_ADDRESS);
475 476 477 478
    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);
479 480 481 482
    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);
483

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

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

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

517 518
    register_event(mp, Vout);

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

522 523
    register_event(mp, MediaChanged);

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

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

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

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

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

    return p_mi;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

642
    libvlc_media_release( p_mi->p_md );
643

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

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

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

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

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

666 667 668
}

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

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

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

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

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

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

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

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

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

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

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

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

774
    vlc_object_release( p_input_thread );
775
}
776

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


1000 1001 1002
/**************************************************************************
 * Getters for stream information
 **************************************************************************/
1003
libvlc_time_t libvlc_media_player_get_length(
Rémi Denis-Courmont's avatar
good  
Rémi Denis-Courmont committed
1004
                             libvlc_media_player_t *p_mi )