media_player.c 58.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-2015 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
52 53 54 55
input_scrambled_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
56
input_event_changed( vlc_object_t * p_this, char const * psz_cmd,
57 58 59
                     vlc_value_t oldval, vlc_value_t newval,
                     void * p_userdata );

60 61 62 63 64 65 66 67 68 69
static int
input_es_changed( vlc_object_t * p_this, char const * psz_cmd,
                  int action, vlc_value_t *p_val,
                  void *p_userdata);

static int
input_es_selected( vlc_object_t * p_this, char const * psz_cmd,
                   vlc_value_t oldval, vlc_value_t newval,
                   void * p_userdata );

70 71 72 73
static int
corks_changed(vlc_object_t *obj, const char *name, vlc_value_t old,
              vlc_value_t cur, void *opaque);

74 75 76 77
static int
mute_changed(vlc_object_t *obj, const char *name, vlc_value_t old,
             vlc_value_t cur, void *opaque);

78 79 80 81 82 83
static void
add_es_callbacks( input_thread_t *p_input_thread, libvlc_media_player_t *p_mi );

static void
del_es_callbacks( input_thread_t *p_input_thread, libvlc_media_player_t *p_mi );

84 85 86
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 );
87

88
static void libvlc_media_player_destroy( libvlc_media_player_t *p_mi );
89

90 91 92 93 94 95 96
/*
 * 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)
{
97
    libvlc_event_manager_register_event_type(mp->p_event_manager, type);
98 99
}

100 101 102 103 104 105 106 107 108
/*
 * 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.
 */
109 110 111 112 113 114 115 116 117 118
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);
}

119 120 121 122 123 124 125 126 127 128
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);
}

129
/*
JP Dinger's avatar
JP Dinger committed
130
 * Release the associated input thread.
131 132
 *
 * Object lock is NOT held.
133
 * Input lock is held or instance is being destroyed.
134
 */
135
static void release_input_thread( libvlc_media_player_t *p_mi )
136
{
137
    assert( p_mi );
138

139
    input_thread_t *p_input_thread = p_mi->input.p_thread;
140
    if( !p_input_thread )
141
        return;
142
    p_mi->input.p_thread = NULL;
143

144 145 146 147
    var_DelCallback( p_input_thread, "can-seek",
                     input_seekable_changed, p_mi );
    var_DelCallback( p_input_thread, "can-pause",
                    input_pausable_changed, p_mi );
148 149
    var_DelCallback( p_input_thread, "program-scrambled",
                    input_scrambled_changed, p_mi );
150 151
    var_DelCallback( p_input_thread, "intf-event",
                     input_event_changed, p_mi );
152
    del_es_callbacks( p_input_thread, p_mi );
153 154

    /* We owned this one */
155
    input_Stop( p_input_thread );
156
    input_Close( p_input_thread );
157 158
}

159 160
/*
 * Retrieve the input thread. Be sure to release the object
161
 * once you are done with it. (libvlc Internal)
162
 */
163
input_thread_t *libvlc_get_input_thread( libvlc_media_player_t *p_mi )
Clément Stenac's avatar
Clément Stenac committed
164 165 166
{
    input_thread_t *p_input_thread;

167
    assert( p_mi );
168

169 170
    lock_input(p_mi);
    p_input_thread = p_mi->input.p_thread;
171 172
    if( p_input_thread )
        vlc_object_hold( p_input_thread );
173 174
    else
        libvlc_printerr( "No active input" );
175
    unlock_input(p_mi);
176

177 178 179
    return p_input_thread;
}

180 181 182 183 184
/*
 * Set the internal state of the media_player. (media player Internal)
 *
 * Function will lock the media_player.
 */
185 186 187 188 189
static void set_state( libvlc_media_player_t *p_mi, libvlc_state_t state,
    bool b_locked )
{
    if(!b_locked)
        lock(p_mi);
190
    p_mi->state = state;
191

192 193 194
    libvlc_media_t *media = p_mi->p_md;
    if (media)
        libvlc_media_retain(media);
195

196 197
    if(!b_locked)
        unlock(p_mi);
198

199 200
    if (media)
    {
201 202 203
        // Also set the state of the corresponding media
        // This is strictly for convenience.
        libvlc_media_set_state(media, state);
204 205

        libvlc_media_release(media);
206 207 208
    }
}

209
static int
210
input_seekable_changed( vlc_object_t * p_this, char const * psz_cmd,
211 212 213 214
                        vlc_value_t oldval, vlc_value_t newval,
                        void * p_userdata )
{
    VLC_UNUSED(oldval);
215 216
    VLC_UNUSED(p_this);
    VLC_UNUSED(psz_cmd);
217
    libvlc_media_player_t * p_mi = p_userdata;
218 219
    libvlc_event_t event;

220
    event.type = libvlc_MediaPlayerSeekableChanged;
221
    event.u.media_player_seekable_changed.new_seekable = newval.b_bool;
222 223 224 225 226 227 228 229 230 231 232

    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);
233 234
    VLC_UNUSED(p_this);
    VLC_UNUSED(psz_cmd);
235
    libvlc_media_player_t * p_mi = p_userdata;
236 237
    libvlc_event_t event;

238
    event.type = libvlc_MediaPlayerPausableChanged;
239
    event.u.media_player_pausable_changed.new_pausable = newval.b_bool;
240 241 242 243 244

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

245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
static int
input_scrambled_changed( vlc_object_t * p_this, char const * psz_cmd,
                        vlc_value_t oldval, vlc_value_t newval,
                        void * p_userdata )
{
    VLC_UNUSED(oldval);
    VLC_UNUSED(p_this);
    VLC_UNUSED(psz_cmd);
    libvlc_media_player_t * p_mi = p_userdata;
    libvlc_event_t event;

    event.type = libvlc_MediaPlayerScrambledChanged;
    event.u.media_player_scrambled_changed.new_scrambled = newval.b_bool;

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

263
static int
Laurent Aimar's avatar
Laurent Aimar committed
264
input_event_changed( vlc_object_t * p_this, char const * psz_cmd,
265 266 267
                     vlc_value_t oldval, vlc_value_t newval,
                     void * p_userdata )
{
268
    VLC_UNUSED(oldval);
Laurent Aimar's avatar
Laurent Aimar committed
269
    input_thread_t * p_input = (input_thread_t *)p_this;
270
    libvlc_media_player_t * p_mi = p_userdata;
Laurent Aimar's avatar
Laurent Aimar committed
271
    libvlc_event_t event;
272

273
    assert( !strcmp( psz_cmd, "intf-event" ) );
274

275 276
    if( newval.i_int == INPUT_EVENT_STATE )
    {
277
        libvlc_state_t libvlc_state;
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308

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

310
        set_state( p_mi, libvlc_state, false );
311 312
        libvlc_event_send( p_mi->p_event_manager, &event );
    }
313
    else if( newval.i_int == INPUT_EVENT_DEAD )
314
    {
315
        libvlc_state_t libvlc_state = libvlc_Ended;
316 317
        event.type = libvlc_MediaPlayerStopped;

318
        set_state( p_mi, libvlc_state, false );
319 320
        libvlc_event_send( p_mi->p_event_manager, &event );
    }
321
    else if( newval.i_int == INPUT_EVENT_POSITION )
322 323 324 325 326 327
    {
        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
328 329
        event.u.media_player_position_changed.new_position =
                                          var_GetFloat( p_input, "position" );
330 331 332 333
        libvlc_event_send( p_mi->p_event_manager, &event );

        /* */
        event.type = libvlc_MediaPlayerTimeChanged;
JP Dinger's avatar
JP Dinger committed
334
        event.u.media_player_time_changed.new_time =
335
           from_mtime(var_GetInteger( p_input, "time" ));
336 337
        libvlc_event_send( p_mi->p_event_manager, &event );
    }
338 339 340 341
    else if( newval.i_int == INPUT_EVENT_LENGTH )
    {
        event.type = libvlc_MediaPlayerLengthChanged;
        event.u.media_player_length_changed.new_length =
342
           from_mtime(var_GetInteger( p_input, "length" ));
343 344
        libvlc_event_send( p_mi->p_event_manager, &event );
    }
345 346 347 348 349 350 351
    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 );
    }
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370
    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
371

372 373 374
    return VLC_SUCCESS;
}

375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453
static int track_type_from_name(const char *psz_name)
{
   if( !strcmp( psz_name, "video-es" ) )
       return libvlc_track_video;
    else if( !strcmp( psz_name, "audio-es" ) )
        return libvlc_track_audio;
    else if( !strcmp( psz_name, "spu-es" ) )
        return libvlc_track_text;
    else
        return libvlc_track_unknown;
}

static int input_es_changed( vlc_object_t *p_this,
                             char const *psz_cmd,
                             int action,
                             vlc_value_t *p_val,
                             void *p_userdata )
{
    VLC_UNUSED(p_this);
    libvlc_media_player_t *mp = p_userdata;
    libvlc_event_t event;

    /* Ignore the "Disable" element */
    if (p_val && p_val->i_int < 0)
        return VLC_EGENERIC;

    switch (action)
    {
    case VLC_VAR_ADDCHOICE:
        event.type = libvlc_MediaPlayerESAdded;
        break;
    case VLC_VAR_DELCHOICE:
    case VLC_VAR_CLEARCHOICES:
        event.type = libvlc_MediaPlayerESDeleted;
        break;
    default:
        return VLC_EGENERIC;
    }

    event.u.media_player_es_changed.i_type = track_type_from_name(psz_cmd);

    int i_id;
    if (action != VLC_VAR_CLEARCHOICES)
    {
        if (!p_val)
            return VLC_EGENERIC;
        i_id = p_val->i_int;
    }
    else
    {
        /* -1 means all ES tracks of this type were deleted. */
        i_id = -1;
    }
    event.u.media_player_es_changed.i_id = i_id;

    libvlc_event_send(mp->p_event_manager, &event);

    return VLC_SUCCESS;
}

static int
input_es_selected( vlc_object_t * p_this, char const * psz_cmd,
                   vlc_value_t oldval, vlc_value_t newval,
                   void * p_userdata )
{
    VLC_UNUSED(p_this);
    VLC_UNUSED(oldval);
    libvlc_media_player_t *mp = p_userdata;
    libvlc_event_t event;

    event.type = libvlc_MediaPlayerESSelected;
    event.u.media_player_es_changed.i_type = track_type_from_name(psz_cmd);
    event.u.media_player_es_changed.i_id = newval.i_int;

    libvlc_event_send(mp->p_event_manager, &event);

    return VLC_SUCCESS;
}

454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472
/**************************************************************************
 * 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;
}

473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489
static int corks_changed(vlc_object_t *obj, const char *name, vlc_value_t old,
                         vlc_value_t cur, void *opaque)
{
    libvlc_media_player_t *mp = (libvlc_media_player_t *)obj;

    if (!old.i_int != !cur.i_int)
    {
        libvlc_event_t event;

        event.type = cur.i_int ? libvlc_MediaPlayerCorked
                               : libvlc_MediaPlayerUncorked;
        libvlc_event_send(mp->p_event_manager, &event);
    }
    VLC_UNUSED(name); VLC_UNUSED(opaque);
    return VLC_SUCCESS;
}

490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506
static int mute_changed(vlc_object_t *obj, const char *name, vlc_value_t old,
                        vlc_value_t cur, void *opaque)
{
    libvlc_media_player_t *mp = (libvlc_media_player_t *)obj;

    if (old.b_bool != cur.b_bool)
    {
        libvlc_event_t event;

        event.type = cur.b_bool ? libvlc_MediaPlayerMuted
                                : libvlc_MediaPlayerUnmuted;
        libvlc_event_send(mp->p_event_manager, &event);
    }
    VLC_UNUSED(name); VLC_UNUSED(opaque);
    return VLC_SUCCESS;
}

507
/**************************************************************************
JP Dinger's avatar
JP Dinger committed
508 509 510 511 512 513 514 515 516 517 518 519
 * 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.
520
 **************************************************************************/
521
libvlc_media_player_t *
Rémi Denis-Courmont's avatar
good  
Rémi Denis-Courmont committed
522
libvlc_media_player_new( libvlc_instance_t *instance )
523
{
524
    libvlc_media_player_t * mp;
525

526
    assert(instance);
527

528 529
    mp = vlc_object_create (instance->p_libvlc_int, sizeof(*mp));
    if (unlikely(mp == NULL))
530
    {
531
        libvlc_printerr("Not enough memory");
532 533
        return NULL;
    }
534

535 536 537
    /* Input */
    var_Create (mp, "rate", VLC_VAR_FLOAT|VLC_VAR_DOINHERIT);

538
    /* Video */
539
    var_Create (mp, "vout", VLC_VAR_STRING|VLC_VAR_DOINHERIT);
540
    var_Create (mp, "window", VLC_VAR_STRING);
541 542 543 544
    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);
545 546
    var_Create (mp, "vmem-setup", VLC_VAR_ADDRESS);
    var_Create (mp, "vmem-cleanup", VLC_VAR_ADDRESS);
547 548 549
    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);
550
    var_Create (mp, "vmem-pitch", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
551
    var_Create (mp, "avcodec-hw", VLC_VAR_STRING);
552
    var_Create (mp, "drawable-xid", VLC_VAR_INTEGER);
553
#if defined (_WIN32) || defined (__OS2__)
554
    var_Create (mp, "drawable-hwnd", VLC_VAR_INTEGER);
555 556 557 558 559
#endif
#ifdef __APPLE__
    var_Create (mp, "drawable-agl", VLC_VAR_INTEGER);
    var_Create (mp, "drawable-nsobject", VLC_VAR_ADDRESS);
#endif
560 561 562 563
#ifdef __ANDROID__
    var_Create (mp, "android-jvm", VLC_VAR_ADDRESS);
    var_Create (mp, "drawable-androidwindow", VLC_VAR_ADDRESS);
#endif
564 565 566 567

    var_Create (mp, "keyboard-events", VLC_VAR_BOOL);
    var_SetBool (mp, "keyboard-events", true);
    var_Create (mp, "mouse-events", VLC_VAR_BOOL);
568
    var_SetBool (mp, "mouse-events", true);
569

570
    var_Create (mp, "fullscreen", VLC_VAR_BOOL);
571 572
    var_Create (mp, "autoscale", VLC_VAR_BOOL | VLC_VAR_DOINHERIT);
    var_Create (mp, "zoom", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT);
573 574 575 576
    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);
577

578 579
    var_Create (mp, "vbi-page", VLC_VAR_INTEGER);
    var_SetInteger (mp, "vbi-page", 100);
580

581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598
    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);

599 600
    var_Create (mp, "contrast", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT);
    var_Create (mp, "brightness", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT);
601
    var_Create (mp, "hue", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT);
602 603 604
    var_Create (mp, "saturation", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT);
    var_Create (mp, "gamma", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT);

605
     /* Audio */
606
    var_Create (mp, "aout", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
607
    var_Create (mp, "mute", VLC_VAR_BOOL);
608
    var_Create (mp, "volume", VLC_VAR_FLOAT);
609
    var_Create (mp, "corks", VLC_VAR_INTEGER);
610
    var_Create (mp, "audio-filter", VLC_VAR_STRING);
611 612
    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
613
    var_Create (mp, "amem-cleanup", VLC_VAR_ADDRESS);
614
    var_Create (mp, "amem-play", VLC_VAR_ADDRESS);
615 616 617 618
    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);
619 620 621 622
    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);
623

624 625 626 627 628
    /* 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
629 630
    /* Equalizer */
    var_Create (mp, "equalizer-preamp", VLC_VAR_FLOAT);
631
    var_Create (mp, "equalizer-vlcfreqs", VLC_VAR_BOOL);
Mark Lee's avatar
Mark Lee committed
632 633
    var_Create (mp, "equalizer-bands", VLC_VAR_STRING);

634
    mp->p_md = NULL;
635
    mp->state = libvlc_NothingSpecial;
636
    mp->p_libvlc_instance = instance;
637
    mp->input.p_thread = NULL;
638 639 640 641 642 643
    mp->input.p_resource = input_resource_New(VLC_OBJECT(mp));
    if (unlikely(mp->input.p_resource == NULL))
    {
        vlc_object_release(mp);
        return NULL;
    }
644 645 646 647
    audio_output_t *aout = input_resource_GetAout(mp->input.p_resource);
    if( aout != NULL )
        input_resource_PutAout(mp->input.p_resource, aout);

648
    vlc_mutex_init (&mp->input.lock);
649
    mp->i_refcount = 1;
650 651
    mp->p_event_manager = libvlc_event_manager_new(mp, instance);
    if (unlikely(mp->p_event_manager == NULL))
652
    {
653
        input_resource_Release(mp->input.p_resource);
654
        vlc_object_release(mp);
655 656
        return NULL;
    }
657
    vlc_mutex_init(&mp->object_lock);
658

659 660 661 662 663 664 665 666 667 668
    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);
669
    register_event(mp, SeekableChanged);
670 671 672 673 674 675

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

677
    register_event(mp, Vout);
678
    register_event(mp, ScrambledChanged);
679 680 681
    register_event(mp, ESAdded);
    register_event(mp, ESDeleted);
    register_event(mp, ESSelected);
682 683
    register_event(mp, Corked);
    register_event(mp, Uncorked);
684 685
    register_event(mp, Muted);
    register_event(mp, Unmuted);
686 687

    var_AddCallback(mp, "corks", corks_changed, NULL);
688
    var_AddCallback(mp, "mute", mute_changed, NULL);
689

690
    /* Snapshot initialization */
691
    register_event(mp, SnapshotTaken);
Jean-Paul Saman's avatar
Jean-Paul Saman committed
692

693 694
    register_event(mp, MediaChanged);

695
    /* Attach a var callback to the global object to provide the glue between
696 697 698 699 700 701
     * 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.
     */
702
    var_AddCallback(mp->p_libvlc, "snapshot-file", snapshot_was_taken, mp);
703

704
    libvlc_retain(instance);
705
    return mp;
706 707 708
}

/**************************************************************************
JP Dinger's avatar
JP Dinger committed
709
 * Create a Media Instance object with a media descriptor.
710
 **************************************************************************/
711
libvlc_media_player_t *
Rémi Denis-Courmont's avatar
good  
Rémi Denis-Courmont committed
712
libvlc_media_player_new_from_media( libvlc_media_t * p_md )
713
{
714
    libvlc_media_player_t * p_mi;
715

Rémi Denis-Courmont's avatar
good  
Rémi Denis-Courmont committed
716
    p_mi = libvlc_media_player_new( p_md->p_libvlc_instance );
717
    if( !p_mi )
718 719
        return NULL;

720
    libvlc_media_retain( p_md );
721
    p_mi->p_md = p_md;
722 723 724 725 726

    return p_mi;
}

/**************************************************************************
727
 * Destroy a Media Instance object (libvlc internal)
728
 *
JP Dinger's avatar
JP Dinger committed
729
 * Warning: No lock held here, but hey, this is internal. Caller must lock.
730
 **************************************************************************/
731
static void libvlc_media_player_destroy( libvlc_media_player_t *p_mi )
732
{
733
    assert( p_mi );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
734

735
    /* Detach Callback from the main libvlc object */
736
    var_DelCallback( p_mi->p_libvlc,
737
                     "snapshot-file", snapshot_was_taken, p_mi );
738

739
    /* Detach callback from the media player / input manager object */
740
    var_DelCallback( p_mi, "mute", mute_changed, NULL );
741 742
    var_DelCallback( p_mi, "corks", corks_changed, NULL );

743 744
    /* No need for lock_input() because no other threads knows us anymore */
    if( p_mi->input.p_thread )
745
        release_input_thread(p_mi);
746 747
    input_resource_Terminate( p_mi->input.p_resource );
    input_resource_Release( p_mi->input.p_resource );
748
    vlc_mutex_destroy( &p_mi->input.lock );
749

750
    libvlc_event_manager_release( p_mi->p_event_manager );
751
    libvlc_media_release( p_mi->p_md );
752
    vlc_mutex_destroy( &p_mi->object_lock );
753 754

    libvlc_instance_t *instance = p_mi->p_libvlc_instance;
755
    vlc_object_release( p_mi );
756
    libvlc_release(instance);
757 758 759
}

/**************************************************************************
JP Dinger's avatar
JP Dinger committed
760 761 762
 * Release a Media Instance object.
 *
 * Function does the locking.
763
 **************************************************************************/
764
void libvlc_media_player_release( libvlc_media_player_t *p_mi )
765
{
766
    bool destroy;
767

768
    assert( p_mi );
769
    lock(p_mi);
770
    destroy = !--p_mi->i_refcount;
771
    unlock(p_mi);
772

773 774
    if( destroy )
        libvlc_media_player_destroy( p_mi );
775
}
776

777
/**************************************************************************
JP Dinger's avatar
JP Dinger committed
778 779 780
 * Retain a Media Instance object.
 *
 * Caller must hold the lock.
781
 **************************************************************************/
782
void libvlc_media_player_retain( libvlc_media_player_t *p_mi )
783
{
784
    assert( p_mi );
785

786
    lock(p_mi);
787
    p_mi->i_refcount++;
788
    unlock(p_mi);
789
}
790

791
/**************************************************************************
JP Dinger's avatar
JP Dinger committed
792 793 794
 * Set the Media descriptor associated with the instance.
 *
 * Enter without lock -- function will lock the object.
795
 **************************************************************************/
796 797
void libvlc_media_player_set_media(
                            libvlc_media_player_t *p_mi,
798
                            libvlc_media_t *p_md )
799
{
800
    lock_input(p_mi);
801

802
    release_input_thread( p_mi );
803

804
    lock( p_mi );
805
    set_state( p_mi, libvlc_NothingSpecial, true );
806
    unlock_input( p_mi );
807

808
    libvlc_media_release( p_mi->p_md );
809

810 811 812
    if( !p_md )
    {
        p_mi->p_md = NULL;
813
        unlock(p_mi);
814 815 816
        return; /* It is ok to pass a NULL md */
    }

817
    libvlc_media_retain( p_md );
818
    p_mi->p_md = p_md;
819

820 821 822 823
    /* 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;

824
    unlock(p_mi);
825 826 827 828 829 830

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

832 833 834
}

/**************************************************************************
JP Dinger's avatar
JP Dinger committed
835
 * Get the Media descriptor associated with the instance.
836
 **************************************************************************/
837
libvlc_media_t *
838
libvlc_media_player_get_media( libvlc_media_player_t *p_mi )
839
{
840
    libvlc_media_t *p_m;
841

842
    lock( p_mi );
843 844
    p_m = p_mi->p_md;
    if( p_m )
845 846 847 848
        libvlc_media_retain( p_m );
    unlock( p_mi );

    return p_m;
849 850
}

851
/**************************************************************************
JP Dinger's avatar
JP Dinger committed
852
 * Get the event Manager.
853 854
 **************************************************************************/
libvlc_event_manager_t *
855
libvlc_media_player_event_manager( libvlc_media_player_t *p_mi )
856 857 858 859
{
    return p_mi->p_event_manager;
}

860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879
static void add_es_callbacks( input_thread_t *p_input_thread, libvlc_media_player_t *p_mi )
{
    var_AddListCallback( p_input_thread, "video-es", input_es_changed, p_mi );
    var_AddListCallback( p_input_thread, "audio-es", input_es_changed, p_mi );
    var_AddListCallback( p_input_thread, "spu-es", input_es_changed, p_mi );
    var_AddCallback( p_input_thread, "video-es", input_es_selected, p_mi );
    var_AddCallback( p_input_thread, "audio-es", input_es_selected, p_mi );
    var_AddCallback( p_input_thread, "spu-es", input_es_selected, p_mi );
}

static void del_es_callbacks( input_thread_t *p_input_thread, libvlc_media_player_t *p_mi )
{
    var_DelListCallback( p_input_thread, "video-es", input_es_changed, p_mi );
    var_DelListCallback( p_input_thread, "audio-es", input_es_changed, p_mi );
    var_DelListCallback( p_input_thread, "spu-es", input_es_changed, p_mi );
    var_DelCallback( p_input_thread, "video-es", input_es_selected, p_mi );
    var_DelCallback( p_input_thread, "audio-es", input_es_selected, p_mi );
    var_DelCallback( p_input_thread, "spu-es", input_es_selected, p_mi );
}

880
/**************************************************************************
JP Dinger's avatar
JP Dinger committed
881
 * Tell media player to start playing.
882
 **************************************************************************/
Rémi Denis-Courmont's avatar
good  
Rémi Denis-Courmont committed
883
int libvlc_media_player_play( libvlc_media_player_t *p_mi )
884
{
885
    lock_input( p_mi );
886

887 888
    input_thread_t *p_input_thread = p_mi->input.p_thread;
    if( p_input_thread )
889
    {
Sam Hocevar's avatar
Sam Hocevar committed
890
        /* A thread already exists, send it a play message */
891
        input_Control( p_input_thread, INPUT_SET_STATE, PLAYING_S );
892
        unlock_input( p_mi );
Rémi Denis-Courmont's avatar
good  
Rémi Denis-Courmont committed
893
        return 0;
894 895
    }

896
    /* Ignore previous exception */
897
    lock(p_mi);
898

899 900
    if( !p_mi->p_md )
    {
901
        unlock(p_mi);
902
        unlock_input( p_mi );
903
        libvlc_printerr( "No associated media descriptor" );
Rémi Denis-Courmont's avatar
good  
Rémi Denis-Courmont committed
904
        return -1;
905 906
    }

907 908 909 910
    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 )
911
    {
912
        unlock_input(p_mi);
Rémi Denis-Courmont's avatar
good  
Rémi Denis-Courmont committed
913 914
        libvlc_printerr( "Not enough memory" );
        return -1;
915
    }
916

917 918
    var_AddCallback( p_input_thread, "can-seek", input_seekable_changed, p_mi );
    var_AddCallback( p_input_thread, "can-pause", input_pausable_changed, p_mi );
919
    var_AddCallback( p_input_thread, "program-scrambled", input_scrambled_changed, p_mi );
Laurent Aimar's avatar
Laurent Aimar committed
920
    var_AddCallback( p_input_thread, "intf-event", input_event_changed, p_mi );
921
    add_es_callbacks( p_input_thread, p_mi );
922

923 924
    if( input_Start( p_input_thread ) )
    {
925
        unlock_input(p_mi);
926
        del_es_callbacks( p_input_thread, p_mi );
927 928
        var_DelCallback( p_input_thread, "intf-event", input_event_changed, p_mi );
        var_DelCallback( p_input_thread, "can-pause", input_pausable_changed, p_mi );
929
        var_DelCallback( p_input_thread, "program-scrambled", input_scrambled_changed, p_mi );
930
        var_DelCallback( p_input_thread, "can-seek", input_seekable_changed, p_mi );
931
        input_Close( p_input_thread );
932 933
        libvlc_printerr( "Input initialization failure" );
        return -1;
934
    }
935 936
    p_mi->input.p_thread = p_input_thread;
    unlock_input(p_mi);
Rémi Denis-Courmont's avatar
good  
Rémi Denis-Courmont committed
937
    return 0;
938 939
}

940
void libvlc_media_player_set_pause( libvlc_media_player_t *p_mi, int paused )
941
{
942
    input_thread_t * p_input_thread = libvlc_get_input_thread( p_mi );
943
    if( !p_input_thread )
944 945
        return;

946
    libvlc_state_t state = libvlc_media_player_get_state( p_mi );
947
    if( state == libvlc_Playing || state == libvlc_Buffering )
948
    {
949 950 951 952 953
        if( paused )
        {
            if( libvlc_media_player_can_pause( p_mi ) )
                input_Control( p_input_thread, INPUT_SET_STATE, PAUSE_S );
            else
954
                input_Stop( p_input_thread );
955
        }
956
    }
957
    else
958 959 960 961
    {
        if( !paused )
            input_Control( p_input_thread, INPUT_SET_STATE, PLAYING_S );
    }
962

963
    vlc_object_release( p_input_thread );
964
}
965

966 967 968 969 970 971 972 973 974 975 976
/**************************************************************************
 * 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 );
}

977
/**************************************************************************
JP Dinger's avatar
JP Dinger committed
978 979 980
 * Tells whether the media player is currently playing.
 *
 * Enter with lock held.
981
 **************************************************************************/
982
int libvlc_media_player_is_playing( libvlc_media_player_t *p_mi )
983
{
984
    libvlc_state_t state = libvlc_media_player_get_state( p_mi );
985
    return (libvlc_Playing == state) || (libvlc_Buffering == state);
986 987
}

988
/**************************************************************************
JP Dinger's avatar
JP Dinger committed
989
 * Stop playing.
990
 **************************************************************************/
991
void libvlc_media_player_stop( libvlc_media_player_t *p_mi )
992
{
993
    lock_input(p_mi);
994
    release_input_thread( p_mi ); /* This will stop the input thread */
995 996 997

    /* Force to go to stopped state, in case we were in Ended, or Error
     * state. */
998
    if( p_mi->state != libvlc_Stopped )
999
    {
1000
        set_state( p_mi, libvlc_Stopped, false );
1001 1002 1003

        /* Construct and send the event */
        libvlc_event_t event;
1004
        event.type = libvlc_MediaPlayerStopped;
1005 1006
        libvlc_event_send( p_mi->p_event_manager, &event );
    }
1007

1008
    input_resource_Terminate( p_mi->input.p_resource );
1009
    unlock_input(p_mi);
1010 1011
}

1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022

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 );
1023
    var_SetString( mp, "avcodec-hw", "none" );
1024 1025
    var_SetString( mp, "vout", "vmem" );
    var_SetString( mp, "window", "none" );
1026 1027
}

1028 1029 1030 1031 1032 1033 1034 1035
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 );
}

1036 1037 1038 1039 1040 1041 1042 1043 1044
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 );
}

1045 1046 1047 1048
/**************************************************************************
 * set_nsobject
 **************************************************************************/
void libvlc_media_player_set_nsobject( libvlc_media_player_t *p_mi,
1049
                                        void * drawable )
1050
{
1051 1052
    assert (p_mi != NULL);
#ifdef __APPLE__
1053 1054 1055
    var_SetString (p_mi, "avcodec-hw", "");
    var_SetString (p_mi, "vout", "");
    var_SetString (p_mi, "window", "");
1056 1057 1058 1059
    var_SetAddress (p_mi, "drawable-nsobject", drawable);
#else
    (void) p_mi; (void)drawable;
#endif
1060 1061 1062
}

/**************************************************************************
1063
 * get_nsobject