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

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

28
#include <assert.h>
29

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

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

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

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

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


59
static int
60 61 62 63 64 65 66 67
input_seekable_changed( vlc_object_t * p_this, char const * psz_cmd,
                        vlc_value_t oldval, vlc_value_t newval,
                        void * p_userdata );
static int
input_pausable_changed( vlc_object_t * p_this, char const * psz_cmd,
                        vlc_value_t oldval, vlc_value_t newval,
                        void * p_userdata );
static int
Laurent Aimar's avatar
Laurent Aimar committed
68
input_event_changed( vlc_object_t * p_this, char const * psz_cmd,
69 70 71
                     vlc_value_t oldval, vlc_value_t newval,
                     void * p_userdata );

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

76
static void libvlc_media_player_destroy( libvlc_media_player_t *p_mi );
77

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

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

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

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

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

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

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

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

132 133 134 135 136 137 138 139 140
    var_DelCallback( p_input_thread, "can-seek",
                     input_seekable_changed, p_mi );
    var_DelCallback( p_input_thread, "can-pause",
                    input_pausable_changed, p_mi );
    var_DelCallback( p_input_thread, "intf-event",
                     input_event_changed, p_mi );

    /* We owned this one */
    input_Stop( p_input_thread, b_input_abort );
141
    input_Close( p_input_thread );
142 143
}

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

152
    assert( p_mi );
153

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

162 163 164
    return p_input_thread;
}

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

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

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

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

        libvlc_media_release(media);
191 192 193
    }
}

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

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

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

static int
input_pausable_changed( vlc_object_t * p_this, char const * psz_cmd,
                        vlc_value_t oldval, vlc_value_t newval,
                        void * p_userdata )
{
    VLC_UNUSED(oldval);
218 219
    VLC_UNUSED(p_this);
    VLC_UNUSED(psz_cmd);
220
    libvlc_media_player_t * p_mi = p_userdata;
221 222
    libvlc_event_t event;

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

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

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

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

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

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

            default:
                return VLC_SUCCESS;
        }
276

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

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

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

        /* */
        event.type = libvlc_MediaPlayerTimeChanged;
JP Dinger's avatar
JP Dinger committed
301
        event.u.media_player_time_changed.new_time =
302
           from_mtime(var_GetTime( p_input, "time" ));
303 304
        libvlc_event_send( p_mi->p_event_manager, &event );
    }
305 306 307 308
    else if( newval.i_int == INPUT_EVENT_LENGTH )
    {
        event.type = libvlc_MediaPlayerLengthChanged;
        event.u.media_player_length_changed.new_length =
309
           from_mtime(var_GetTime( p_input, "length" ));
310 311
        libvlc_event_send( p_mi->p_event_manager, &event );
    }
312 313 314 315 316 317 318
    else if( newval.i_int == INPUT_EVENT_CACHE )
    {
        event.type = libvlc_MediaPlayerBuffering;
        event.u.media_player_buffering.new_cache = (int)(100 *
            var_GetFloat( p_input, "cache" ));
        libvlc_event_send( p_mi->p_event_manager, &event );
    }
319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
    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
338

339 340 341
    return VLC_SUCCESS;
}

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

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

    return libvlc_get_input_thread (mp);
366 367
}

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

371

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

391
    assert(instance);
392

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

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

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

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

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

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

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

466
     /* Audio */
467
    var_Create (mp, "aout", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
468
    var_Create (mp, "mute", VLC_VAR_BOOL);
469
    var_Create (mp, "volume", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
470 471
    var_Create (mp, "find-input-callback", VLC_VAR_ADDRESS);
    var_SetAddress (mp, "find-input-callback", find_input);
472 473 474 475
    var_Create (mp, "amem-data", VLC_VAR_ADDRESS);
    var_Create (mp, "amem-setup", VLC_VAR_ADDRESS);
    var_Create (mp, "amem-close", VLC_VAR_ADDRESS);
    var_Create (mp, "amem-play", VLC_VAR_ADDRESS);
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
    /* Snapshot initialization */
519
    register_event(mp, SnapshotTaken);
Jean-Paul Saman's avatar
Jean-Paul Saman committed
520

521 522
    register_event(mp, MediaChanged);

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

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

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

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

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

    return p_mi;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

641
    libvlc_media_release( p_mi->p_md );
642

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

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

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

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

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

665 666 667
}

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

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

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

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

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

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

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

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

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

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

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

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

773
    vlc_object_release( p_input_thread );
774
}
775

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


999 1000 1001
/**************************************************************************
 * Getters for stream information
 **************************************************************************/
1002
libvlc_time_t libvlc_media_player_get_length(
Rémi Denis-Courmont's avatar
good  
Rémi Denis-Courmont committed
1003
                             libvlc_media_player_t *p_mi )
1004 1005
{
    input_thread_t *p_input_thread;
1006
    libvlc_time_t i_time;
1007

1008
    p_input_thread = libvlc_get_input_thread ( p_mi );
1009
    if( !p_input_thread )
Jean-Paul Saman's avatar
Jean-Paul Saman committed
1010 1011
        return -1;

1012
    i_time = from_mtime(var_GetTime( p_input_thread, "length" ));
Clément Stenac's avatar
Clément Stenac committed
1013 1014
    vlc_object_release( p_input_thread );

1015
    return i_time;
Clément Stenac's avatar
Clément Stenac committed
1016 1017
}