media_player.c 45.5 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>
Mark Lee's avatar
Mark Lee committed
36
#include <vlc_aout.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
static int
44 45 46 47 48 49 50 51
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
52
input_event_changed( vlc_object_t * p_this, char const * psz_cmd,
53 54 55
                     vlc_value_t oldval, vlc_value_t newval,
                     void * p_userdata );

56 57 58
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 );
59

60
static void libvlc_media_player_destroy( libvlc_media_player_t *p_mi );
61

62 63 64 65 66 67 68
/*
 * 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)
{
69
    libvlc_event_manager_register_event_type(mp->p_event_manager, type);
70 71
}

72 73 74 75 76 77 78 79 80
/*
 * 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.
 */
81 82 83 84 85 86 87 88 89 90
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);
}

91 92 93 94 95 96 97 98 99 100
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);
}

101
/*
JP Dinger's avatar
JP Dinger committed
102
 * Release the associated input thread.
103 104
 *
 * Object lock is NOT held.
105
 * Input lock is held or instance is being destroyed.
106
 */
107
static void release_input_thread( libvlc_media_player_t *p_mi, bool b_input_abort )
108
{
109
    assert( p_mi );
110

111
    input_thread_t *p_input_thread = p_mi->input.p_thread;
112
    if( !p_input_thread )
113
        return;
114
    p_mi->input.p_thread = NULL;
115

116 117 118 119 120 121 122 123 124
    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 );
125
    input_Close( p_input_thread );
126 127
}

128 129
/*
 * Retrieve the input thread. Be sure to release the object
130
 * once you are done with it. (libvlc Internal)
131
 */
132
input_thread_t *libvlc_get_input_thread( libvlc_media_player_t *p_mi )
Clément Stenac's avatar
Clément Stenac committed
133 134 135
{
    input_thread_t *p_input_thread;

136
    assert( p_mi );
137

138 139
    lock_input(p_mi);
    p_input_thread = p_mi->input.p_thread;
140 141
    if( p_input_thread )
        vlc_object_hold( p_input_thread );
142 143
    else
        libvlc_printerr( "No active input" );
144
    unlock_input(p_mi);
145

146 147 148
    return p_input_thread;
}

149 150 151 152 153
/*
 * Set the internal state of the media_player. (media player Internal)
 *
 * Function will lock the media_player.
 */
154 155 156 157 158
static void set_state( libvlc_media_player_t *p_mi, libvlc_state_t state,
    bool b_locked )
{
    if(!b_locked)
        lock(p_mi);
159
    p_mi->state = state;
160

161 162 163
    libvlc_media_t *media = p_mi->p_md;
    if (media)
        libvlc_media_retain(media);
164

165 166
    if(!b_locked)
        unlock(p_mi);
167

168 169
    if (media)
    {
170 171 172
        // Also set the state of the corresponding media
        // This is strictly for convenience.
        libvlc_media_set_state(media, state);
173 174

        libvlc_media_release(media);
175 176 177
    }
}

178
static int
179
input_seekable_changed( vlc_object_t * p_this, char const * psz_cmd,
180 181 182 183
                        vlc_value_t oldval, vlc_value_t newval,
                        void * p_userdata )
{
    VLC_UNUSED(oldval);
184 185
    VLC_UNUSED(p_this);
    VLC_UNUSED(psz_cmd);
186
    libvlc_media_player_t * p_mi = p_userdata;
187 188
    libvlc_event_t event;

189
    event.type = libvlc_MediaPlayerSeekableChanged;
190
    event.u.media_player_seekable_changed.new_seekable = newval.b_bool;
191 192 193 194 195 196 197 198 199 200 201

    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);
202 203
    VLC_UNUSED(p_this);
    VLC_UNUSED(psz_cmd);
204
    libvlc_media_player_t * p_mi = p_userdata;
205 206
    libvlc_event_t event;

207
    event.type = libvlc_MediaPlayerPausableChanged;
208
    event.u.media_player_pausable_changed.new_pausable = newval.b_bool;
209 210 211 212 213

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

214
static int
Laurent Aimar's avatar
Laurent Aimar committed
215
input_event_changed( vlc_object_t * p_this, char const * psz_cmd,
216 217 218
                     vlc_value_t oldval, vlc_value_t newval,
                     void * p_userdata )
{
219
    VLC_UNUSED(oldval);
Laurent Aimar's avatar
Laurent Aimar committed
220
    input_thread_t * p_input = (input_thread_t *)p_this;
221
    libvlc_media_player_t * p_mi = p_userdata;
Laurent Aimar's avatar
Laurent Aimar committed
222
    libvlc_event_t event;
223

224
    assert( !strcmp( psz_cmd, "intf-event" ) );
225

226 227
    if( newval.i_int == INPUT_EVENT_STATE )
    {
228
        libvlc_state_t libvlc_state;
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259

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

261
        set_state( p_mi, libvlc_state, false );
262 263
        libvlc_event_send( p_mi->p_event_manager, &event );
    }
264 265 266 267 268
    else if( newval.i_int == INPUT_EVENT_ABORT )
    {
        libvlc_state_t libvlc_state = libvlc_Stopped;
        event.type = libvlc_MediaPlayerStopped;

269
        set_state( p_mi, libvlc_state, false );
270 271
        libvlc_event_send( p_mi->p_event_manager, &event );
    }
272
    else if( newval.i_int == INPUT_EVENT_POSITION )
273 274 275 276 277 278
    {
        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
279 280
        event.u.media_player_position_changed.new_position =
                                          var_GetFloat( p_input, "position" );
281 282 283 284
        libvlc_event_send( p_mi->p_event_manager, &event );

        /* */
        event.type = libvlc_MediaPlayerTimeChanged;
JP Dinger's avatar
JP Dinger committed
285
        event.u.media_player_time_changed.new_time =
286
           from_mtime(var_GetTime( p_input, "time" ));
287 288
        libvlc_event_send( p_mi->p_event_manager, &event );
    }
289 290 291 292
    else if( newval.i_int == INPUT_EVENT_LENGTH )
    {
        event.type = libvlc_MediaPlayerLengthChanged;
        event.u.media_player_length_changed.new_length =
293
           from_mtime(var_GetTime( p_input, "length" ));
294 295
        libvlc_event_send( p_mi->p_event_manager, &event );
    }
296 297 298 299 300 301 302
    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 );
    }
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
    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
322

323 324 325
    return VLC_SUCCESS;
}

326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344
/**************************************************************************
 * 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;
}

345
/* */
346 347
static void libvlc_media_player_destroy( libvlc_media_player_t * );

348

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

368
    assert(instance);
369

370 371
    mp = vlc_object_create (instance->p_libvlc_int, sizeof(*mp));
    if (unlikely(mp == NULL))
372
    {
373
        libvlc_printerr("Not enough memory");
374 375
        return NULL;
    }
376

377 378 379
    /* Input */
    var_Create (mp, "rate", VLC_VAR_FLOAT|VLC_VAR_DOINHERIT);

380
    /* Video */
381
    var_Create (mp, "vout", VLC_VAR_STRING|VLC_VAR_DOINHERIT);
382
    var_Create (mp, "window", VLC_VAR_STRING);
383 384 385 386
    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);
387 388
    var_Create (mp, "vmem-setup", VLC_VAR_ADDRESS);
    var_Create (mp, "vmem-cleanup", VLC_VAR_ADDRESS);
389 390 391
    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);
392
    var_Create (mp, "vmem-pitch", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
393
    var_Create (mp, "drawable-xid", VLC_VAR_INTEGER);
394
#if defined (_WIN32) || defined (__OS2__)
395
    var_Create (mp, "drawable-hwnd", VLC_VAR_INTEGER);
396 397 398 399 400 401 402 403 404
#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);
405
    var_SetBool (mp, "mouse-events", true);
406

407 408 409 410 411 412 413 414 415
    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);
416

417
    var_Create (mp, "vbi-page", VLC_VAR_INTEGER);
418

419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436
    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);

437 438 439 440 441 442
    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);

443
     /* Audio */
444
    var_Create (mp, "aout", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
445
    var_Create (mp, "mute", VLC_VAR_BOOL);
446
    var_Create (mp, "volume", VLC_VAR_FLOAT);
447
    var_Create (mp, "corks", VLC_VAR_INTEGER);
448 449
    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
450
    var_Create (mp, "amem-cleanup", VLC_VAR_ADDRESS);
451
    var_Create (mp, "amem-play", VLC_VAR_ADDRESS);
452 453 454 455
    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);
456 457 458 459
    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);
460

461 462 463 464 465
    /* Video Title */
    var_Create (mp, "video-title-show", VLC_VAR_BOOL);
    var_Create (mp, "video-title-position", VLC_VAR_INTEGER);
    var_Create (mp, "video-title-timeout", VLC_VAR_INTEGER);

Mark Lee's avatar
Mark Lee committed
466 467 468 469
    /* Equalizer */
    var_Create (mp, "equalizer-preamp", VLC_VAR_FLOAT);
    var_Create (mp, "equalizer-bands", VLC_VAR_STRING);

470
    mp->p_md = NULL;
471
    mp->state = libvlc_NothingSpecial;
472
    mp->p_libvlc_instance = instance;
473
    mp->input.p_thread = NULL;
474 475 476 477 478 479
    mp->input.p_resource = input_resource_New(VLC_OBJECT(mp));
    if (unlikely(mp->input.p_resource == NULL))
    {
        vlc_object_release(mp);
        return NULL;
    }
480
    vlc_mutex_init (&mp->input.lock);
481
    mp->i_refcount = 1;
482 483
    mp->p_event_manager = libvlc_event_manager_new(mp, instance);
    if (unlikely(mp->p_event_manager == NULL))
484
    {
485
        input_resource_Release(mp->input.p_resource);
486
        vlc_object_release(mp);
487 488
        return NULL;
    }
489
    vlc_mutex_init(&mp->object_lock);
490

491 492 493 494 495 496 497 498 499 500
    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);
501
    register_event(mp, SeekableChanged);
502 503 504 505 506 507

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

509 510
    register_event(mp, Vout);

511
    /* Snapshot initialization */
512
    register_event(mp, SnapshotTaken);
Jean-Paul Saman's avatar
Jean-Paul Saman committed
513

514 515
    register_event(mp, MediaChanged);

516
    /* Attach a var callback to the global object to provide the glue between
517 518 519 520 521 522
     * 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.
     */
523
    var_AddCallback(mp->p_libvlc, "snapshot-file", snapshot_was_taken, mp);
524

525
    libvlc_retain(instance);
526
    return mp;
527 528 529
}

/**************************************************************************
JP Dinger's avatar
JP Dinger committed
530
 * Create a Media Instance object with a media descriptor.
531
 **************************************************************************/
532
libvlc_media_player_t *
Rémi Denis-Courmont's avatar
good  
Rémi Denis-Courmont committed
533
libvlc_media_player_new_from_media( libvlc_media_t * p_md )
534
{
535
    libvlc_media_player_t * p_mi;
536

Rémi Denis-Courmont's avatar
good  
Rémi Denis-Courmont committed
537
    p_mi = libvlc_media_player_new( p_md->p_libvlc_instance );
538
    if( !p_mi )
539 540
        return NULL;

541
    libvlc_media_retain( p_md );
542
    p_mi->p_md = p_md;
543 544 545 546 547

    return p_mi;
}

/**************************************************************************
548
 * Destroy a Media Instance object (libvlc internal)
549
 *
JP Dinger's avatar
JP Dinger committed
550
 * Warning: No lock held here, but hey, this is internal. Caller must lock.
551
 **************************************************************************/
552
static void libvlc_media_player_destroy( libvlc_media_player_t *p_mi )
553
{
554
    assert( p_mi );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
555

556
    /* Detach Callback from the main libvlc object */
557
    var_DelCallback( p_mi->p_libvlc,
558
                     "snapshot-file", snapshot_was_taken, p_mi );
559

560 561
    /* No need for lock_input() because no other threads knows us anymore */
    if( p_mi->input.p_thread )
562
        release_input_thread(p_mi, true);
563 564
    input_resource_Terminate( p_mi->input.p_resource );
    input_resource_Release( p_mi->input.p_resource );
565
    vlc_mutex_destroy( &p_mi->input.lock );
566

567
    libvlc_event_manager_release( p_mi->p_event_manager );
568
    libvlc_media_release( p_mi->p_md );
569
    vlc_mutex_destroy( &p_mi->object_lock );
570 571

    libvlc_instance_t *instance = p_mi->p_libvlc_instance;
572
    vlc_object_release( p_mi );
573
    libvlc_release(instance);
574 575 576
}

/**************************************************************************
JP Dinger's avatar
JP Dinger committed
577 578 579
 * Release a Media Instance object.
 *
 * Function does the locking.
580
 **************************************************************************/
581
void libvlc_media_player_release( libvlc_media_player_t *p_mi )
582
{
583
    bool destroy;
584

585
    assert( p_mi );
586
    lock(p_mi);
587
    destroy = !--p_mi->i_refcount;
588
    unlock(p_mi);
589

590 591
    if( destroy )
        libvlc_media_player_destroy( p_mi );
592
}
593

594
/**************************************************************************
JP Dinger's avatar
JP Dinger committed
595 596 597
 * Retain a Media Instance object.
 *
 * Caller must hold the lock.
598
 **************************************************************************/
599
void libvlc_media_player_retain( libvlc_media_player_t *p_mi )
600
{
601
    assert( p_mi );
602

603
    lock(p_mi);
604
    p_mi->i_refcount++;
605
    unlock(p_mi);
606
}
607

608
/**************************************************************************
JP Dinger's avatar
JP Dinger committed
609 610 611
 * Set the Media descriptor associated with the instance.
 *
 * Enter without lock -- function will lock the object.
612
 **************************************************************************/
613 614
void libvlc_media_player_set_media(
                            libvlc_media_player_t *p_mi,
615
                            libvlc_media_t *p_md )
616
{
617
    lock_input(p_mi);
618

619 620 621
    /* FIXME I am not sure if it is a user request or on die(eof/error)
     * request here */
    release_input_thread( p_mi,
622 623 624
                          p_mi->input.p_thread &&
                          !p_mi->input.p_thread->b_eof &&
                          !p_mi->input.p_thread->b_error );
625

626
    lock( p_mi );
627
    set_state( p_mi, libvlc_NothingSpecial, true );
628
    unlock_input( p_mi );
629

630
    libvlc_media_release( p_mi->p_md );
631

632 633 634
    if( !p_md )
    {
        p_mi->p_md = NULL;
635
        unlock(p_mi);
636 637 638
        return; /* It is ok to pass a NULL md */
    }

639
    libvlc_media_retain( p_md );
640
    p_mi->p_md = p_md;
641

642 643 644 645
    /* 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;

646
    unlock(p_mi);
647 648 649 650 651 652

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

654 655 656
}

/**************************************************************************
JP Dinger's avatar
JP Dinger committed
657
 * Get the Media descriptor associated with the instance.
658
 **************************************************************************/
659
libvlc_media_t *
660
libvlc_media_player_get_media( libvlc_media_player_t *p_mi )
661
{
662
    libvlc_media_t *p_m;
663

664
    lock(p_mi);
665 666 667
    p_m = p_mi->p_md;
    if( p_m )
        libvlc_media_retain( p_mi->p_md );
668
    unlock(p_mi);
669
    return p_mi->p_md;
670 671
}

672
/**************************************************************************
JP Dinger's avatar
JP Dinger committed
673
 * Get the event Manager.
674 675
 **************************************************************************/
libvlc_event_manager_t *
676
libvlc_media_player_event_manager( libvlc_media_player_t *p_mi )
677 678 679 680
{
    return p_mi->p_event_manager;
}

681
/**************************************************************************
JP Dinger's avatar
JP Dinger committed
682
 * Tell media player to start playing.
683
 **************************************************************************/
Rémi Denis-Courmont's avatar
good  
Rémi Denis-Courmont committed
684
int libvlc_media_player_play( libvlc_media_player_t *p_mi )
685
{
686
    lock_input( p_mi );
687

688 689
    input_thread_t *p_input_thread = p_mi->input.p_thread;
    if( p_input_thread )
690
    {
Sam Hocevar's avatar
Sam Hocevar committed
691
        /* A thread already exists, send it a play message */
692
        input_Control( p_input_thread, INPUT_SET_STATE, PLAYING_S );
693
        unlock_input( p_mi );
Rémi Denis-Courmont's avatar
good  
Rémi Denis-Courmont committed
694
        return 0;
695 696
    }

697
    /* Ignore previous exception */
698
    lock(p_mi);
699

700 701
    if( !p_mi->p_md )
    {
702
        unlock(p_mi);
703
        unlock_input( p_mi );
704
        libvlc_printerr( "No associated media descriptor" );
Rémi Denis-Courmont's avatar
good  
Rémi Denis-Courmont committed
705
        return -1;
706 707
    }

708 709 710 711
    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 )
712
    {
713
        unlock_input(p_mi);
Rémi Denis-Courmont's avatar
good  
Rémi Denis-Courmont committed
714 715
        libvlc_printerr( "Not enough memory" );
        return -1;
716
    }
717

718 719
    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
720
    var_AddCallback( p_input_thread, "intf-event", input_event_changed, p_mi );
721

722 723
    if( input_Start( p_input_thread ) )
    {
724 725 726 727
        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 );
728
        vlc_object_release( p_input_thread );
729 730
        libvlc_printerr( "Input initialization failure" );
        return -1;
731
    }
732 733
    p_mi->input.p_thread = p_input_thread;
    unlock_input(p_mi);
Rémi Denis-Courmont's avatar
good  
Rémi Denis-Courmont committed
734
    return 0;
735 736
}

737
void libvlc_media_player_set_pause( libvlc_media_player_t *p_mi, int paused )
738
{
739
    input_thread_t * p_input_thread = libvlc_get_input_thread( p_mi );
740
    if( !p_input_thread )
741 742
        return;

743
    libvlc_state_t state = libvlc_media_player_get_state( p_mi );
744
    if( state == libvlc_Playing || state == libvlc_Buffering )
745
    {
746 747 748 749 750 751 752
        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 );
        }
753
    }
754
    else
755 756 757 758
    {
        if( !paused )
            input_Control( p_input_thread, INPUT_SET_STATE, PLAYING_S );
    }
759

760
    vlc_object_release( p_input_thread );
761
}
762

763 764 765 766 767 768 769 770 771 772 773
/**************************************************************************
 * 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 );
}

774
/**************************************************************************
JP Dinger's avatar
JP Dinger committed
775 776 777
 * Tells whether the media player is currently playing.
 *
 * Enter with lock held.
778
 **************************************************************************/
779
int libvlc_media_player_is_playing( libvlc_media_player_t *p_mi )
780
{
781
    libvlc_state_t state = libvlc_media_player_get_state( p_mi );
782
    return (libvlc_Playing == state) || (libvlc_Buffering == state);
783 784
}

785
/**************************************************************************
JP Dinger's avatar
JP Dinger committed
786
 * Stop playing.
787
 **************************************************************************/
788
void libvlc_media_player_stop( libvlc_media_player_t *p_mi )
789
{
790
    libvlc_state_t state = libvlc_media_player_get_state( p_mi );
791

792
    lock_input(p_mi);
793 794 795 796 797
    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 )
798
    {
799
        set_state( p_mi, libvlc_Stopped, false );
800 801 802

        /* Construct and send the event */
        libvlc_event_t event;
803
        event.type = libvlc_MediaPlayerStopped;
804 805
        libvlc_event_send( p_mi->p_event_manager, &event );
    }
806

807
    input_resource_Terminate( p_mi->input.p_resource );
808
    unlock_input(p_mi);
809 810
}

811 812 813 814 815 816 817 818 819 820 821 822 823 824

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

825 826 827 828 829 830 831 832
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 );
}

833 834 835 836 837 838 839 840 841
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 );
}

842 843 844 845
/**************************************************************************
 * set_nsobject
 **************************************************************************/
void libvlc_media_player_set_nsobject( libvlc_media_player_t *p_mi,
846
                                        void * drawable )
847
{
848 849 850 851 852 853
    assert (p_mi != NULL);
#ifdef __APPLE__
    var_SetAddress (p_mi, "drawable-nsobject", drawable);
#else
    (void) p_mi; (void)drawable;
#endif
854 855 856
}

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

869 870 871 872
/**************************************************************************
 * set_agl
 **************************************************************************/
void libvlc_media_player_set_agl( libvlc_media_player_t *p_mi,
873
                                  uint32_t drawable )
874
{
875 876 877 878 879
#ifdef __APPLE__
    var_SetInteger (p_mi, "drawable-agl", drawable);
#else
    (void) p_mi; (void)drawable;
#endif
880 881 882 883 884 885 886
}

/**************************************************************************
 * get_agl
 **************************************************************************/
uint32_t libvlc_media_player_get_agl( libvlc_media_player_t *p_mi )
{
887 888 889 890 891 892
    assert (p_mi != NULL);
#ifdef __APPLE__
    return var_GetInteger (p_mi, "drawable-agl");
#else
    return 0;
#endif
893 894
}

JP Dinger's avatar
JP Dinger committed
895 896 897
/**************************************************************************
 * set_xwindow
 **************************************************************************/
898
void libvlc_media_player_set_xwindow( libvlc_media_player_t *p_mi,
899
                                      uint32_t drawable )
900
{
901
    assert (p_mi != NULL);
902 903

    var_SetString (p_mi, "vout", drawable ? "xid" : "any");
904
    var_SetString (p_mi, "window", drawable ? "embed-xid,any" : "any");
905
    var_SetInteger (p_mi, "drawable-xid", drawable);
906 907
}

JP Dinger's avatar
JP Dinger committed
908 909 910
/**************************************************************************
 * get_xwindow
 **************************************************************************/
911 912
uint32_t libvlc_media_player_get_xwindow( libvlc_media_player_t *p_mi )
{
913
    return var_GetInteger (p_mi, "drawable-xid");
914 915
}

JP Dinger's avatar
JP Dinger committed
916 917 918
/**************************************************************************
 * set_hwnd
 **************************************************************************/
919
void libvlc_media_player_set_hwnd( libvlc_media_player_t *p_mi,
920
                                   void *drawable )
921
{
922
    assert (p_mi != NULL);
923
#if defined (_WIN32) || defined (__OS2__)
924 925
    var_SetString (p_mi, "window",
                   (drawable != NULL) ? "embed-hwnd,any" : "");
926
    var_SetInteger (p_mi, "drawable-hwnd", (uintptr_t)drawable);
927 928 929
#else
    (void) p_mi; (void) drawable;
#endif
930 931
}

JP Dinger's avatar
JP Dinger committed
932 933 934
/**************************************************************************
 * get_hwnd
 **************************************************************************/
935 936
void *libvlc_media_player_get_hwnd( libvlc_media_player_t *p_mi )
{
937
    assert (p_mi != NULL);
938
#if defined (_WIN32) || defined (__OS2__)
939
    return (void *)(uintptr_t)var_GetInteger (p_mi, "drawable-hwnd");
940 941 942
#else
    return NULL;
#endif
943 944
}

945 946
void libvlc_audio_set_callbacks( libvlc_media_player_t *mp,
                                 libvlc_audio_play_cb play_cb,
947 948 949 950
                                 libvlc_audio_pause_cb pause_cb,
                                 libvlc_audio_resume_cb resume_cb,
                                 libvlc_audio_flush_cb flush_cb,
                                 libvlc_audio_drain_cb drain_cb,
951 952 953
                                 void *opaque )
{
    var_SetAddress( mp, "amem-play", play_cb );
954 955 956 957
    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 );
958
    var_SetAddress( mp, "amem-data", opaque );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
959
    var_SetString( mp, "aout", "amem,none" );
960 961
}

962 963 964 965 966 967
void libvlc_audio_set_volume_callback( libvlc_media_player_t *mp,
                                       libvlc_audio_set_volume_cb cb )
{
    var_SetAddress( mp, "amem-set-volume", cb );
}

968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984
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 );
}


985 986 987
/**************************************************************************
 * Getters for stream information
 **************************************************************************/
988
libvlc_time_t libvlc_media_player_get_length(
Rémi Denis-Courmont's avatar
good  
Rémi Denis-Courmont committed
989
                             libvlc_media_player_t *p_mi )
990 991
{
    input_thread_t *p_input_thread;
992
    libvlc_time_t i_time;
993

994
    p_input_thread = libvlc_get_input_thread ( p_mi );
995
    if( !p_input_thread )
Jean-Paul Saman's avatar
Jean-Paul Saman committed
996 997
        return -1;

998
    i_time = from_mtime(var_GetTime( p_input_thread, "length" ));
Clément Stenac's avatar
Clément Stenac committed
999 1000
    vlc_object_release( p_input_thread );

1001
    return i_time;
Clément Stenac's avatar
Clément Stenac committed
1002 1003
}

Rémi Denis-Courmont's avatar
good  
Rémi Denis-Courmont committed
1004
libvlc_time_t libvlc_media_player_get_time( libvlc_media_player_t *p_mi )
Clément Stenac's avatar
Clément Stenac committed
1005 1006
{
    input_thread_t *p_input_thread;