media_player.c 59.9 KB
Newer Older
Clément Stenac's avatar
Clément Stenac committed
1
/*****************************************************************************
2
 * media_player.c: Libvlc API Media Instance management functions
Clément Stenac's avatar
Clément Stenac committed
3
 *****************************************************************************
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
static int
volume_changed(vlc_object_t *obj, const char *name, vlc_value_t old,
               vlc_value_t cur, void *opaque);

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

88 89 90
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 );
91

92
static void libvlc_media_player_destroy( libvlc_media_player_t *p_mi );
93

94 95 96 97 98 99 100
/*
 * 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)
{
101
    libvlc_event_manager_register_event_type(mp->p_event_manager, type);
102 103
}

104 105 106 107 108 109 110 111 112
/*
 * 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.
 */
113 114 115 116 117 118 119 120 121 122
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);
}

123 124 125 126 127 128 129 130 131 132
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);
}

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

143
    input_thread_t *p_input_thread = p_mi->input.p_thread;
144
    if( !p_input_thread )
145
        return;
146
    p_mi->input.p_thread = NULL;
147

148 149 150 151
    var_DelCallback( p_input_thread, "can-seek",
                     input_seekable_changed, p_mi );
    var_DelCallback( p_input_thread, "can-pause",
                    input_pausable_changed, p_mi );
152 153
    var_DelCallback( p_input_thread, "program-scrambled",
                    input_scrambled_changed, p_mi );
154 155
    var_DelCallback( p_input_thread, "intf-event",
                     input_event_changed, p_mi );
156
    del_es_callbacks( p_input_thread, p_mi );
157 158

    /* We owned this one */
159
    input_Stop( p_input_thread );
160
    input_Close( p_input_thread );
161 162
}

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

171
    assert( p_mi );
172

173 174
    lock_input(p_mi);
    p_input_thread = p_mi->input.p_thread;
175 176
    if( p_input_thread )
        vlc_object_hold( p_input_thread );
177 178
    else
        libvlc_printerr( "No active input" );
179
    unlock_input(p_mi);
180

181 182 183
    return p_input_thread;
}

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

196 197 198
    libvlc_media_t *media = p_mi->p_md;
    if (media)
        libvlc_media_retain(media);
199

200 201
    if(!b_locked)
        unlock(p_mi);
202

203 204
    if (media)
    {
205 206 207
        // Also set the state of the corresponding media
        // This is strictly for convenience.
        libvlc_media_set_state(media, state);
208 209

        libvlc_media_release(media);
210 211 212
    }
}

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

224
    event.type = libvlc_MediaPlayerSeekableChanged;
225
    event.u.media_player_seekable_changed.new_seekable = newval.b_bool;
226 227 228 229 230 231 232 233 234 235 236

    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);
237 238
    VLC_UNUSED(p_this);
    VLC_UNUSED(psz_cmd);
239
    libvlc_media_player_t * p_mi = p_userdata;
240 241
    libvlc_event_t event;

242
    event.type = libvlc_MediaPlayerPausableChanged;
243
    event.u.media_player_pausable_changed.new_pausable = newval.b_bool;
244 245 246 247 248

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

249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266
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;
}

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

277
    assert( !strcmp( psz_cmd, "intf-event" ) );
278

279 280
    if( newval.i_int == INPUT_EVENT_STATE )
    {
281
        libvlc_state_t libvlc_state;
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 309 310 311 312

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

314
        set_state( p_mi, libvlc_state, false );
315 316
        libvlc_event_send( p_mi->p_event_manager, &event );
    }
317
    else if( newval.i_int == INPUT_EVENT_DEAD )
318
    {
319
        libvlc_state_t libvlc_state = libvlc_Ended;
320 321
        event.type = libvlc_MediaPlayerStopped;

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

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

376 377 378
    return VLC_SUCCESS;
}

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 454 455 456 457
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;
}

458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476
/**************************************************************************
 * 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;
}

477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493
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;
}

494 495 496 497 498 499 500 501 502 503 504 505 506
static int audio_device_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;
    libvlc_event_t event;

    event.type = libvlc_MediaPlayerAudioDevice;
    event.u.media_player_audio_device.device = cur.psz_string;
    libvlc_event_send(mp->p_event_manager, &event);
    VLC_UNUSED(name); VLC_UNUSED(old); VLC_UNUSED(opaque);
    return VLC_SUCCESS;
}

507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523
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;
}

524 525 526 527 528 529 530 531 532 533 534 535 536
static int volume_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;
    libvlc_event_t event;

    event.type = libvlc_MediaPlayerAudioVolume;
    event.u.media_player_audio_volume.volume = cur.f_float;
    libvlc_event_send(mp->p_event_manager, &event);
    VLC_UNUSED(name); VLC_UNUSED(old); VLC_UNUSED(opaque);
    return VLC_SUCCESS;
}

537
/**************************************************************************
JP Dinger's avatar
JP Dinger committed
538 539 540 541 542 543 544 545 546 547 548 549
 * 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.
550
 **************************************************************************/
551
libvlc_media_player_t *
Rémi Denis-Courmont's avatar
good  
Rémi Denis-Courmont committed
552
libvlc_media_player_new( libvlc_instance_t *instance )
553
{
554
    libvlc_media_player_t * mp;
555

556
    assert(instance);
557

558 559
    mp = vlc_object_create (instance->p_libvlc_int, sizeof(*mp));
    if (unlikely(mp == NULL))
560
    {
561
        libvlc_printerr("Not enough memory");
562 563
        return NULL;
    }
564

565 566 567
    /* Input */
    var_Create (mp, "rate", VLC_VAR_FLOAT|VLC_VAR_DOINHERIT);

568
    /* Video */
569
    var_Create (mp, "vout", VLC_VAR_STRING|VLC_VAR_DOINHERIT);
570
    var_Create (mp, "window", VLC_VAR_STRING);
571 572 573 574
    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);
575 576
    var_Create (mp, "vmem-setup", VLC_VAR_ADDRESS);
    var_Create (mp, "vmem-cleanup", VLC_VAR_ADDRESS);
577 578 579
    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);
580
    var_Create (mp, "vmem-pitch", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
581
    var_Create (mp, "avcodec-hw", VLC_VAR_STRING);
582
    var_Create (mp, "drawable-xid", VLC_VAR_INTEGER);
583
#if defined (_WIN32) || defined (__OS2__)
584
    var_Create (mp, "drawable-hwnd", VLC_VAR_INTEGER);
585 586 587 588 589
#endif
#ifdef __APPLE__
    var_Create (mp, "drawable-agl", VLC_VAR_INTEGER);
    var_Create (mp, "drawable-nsobject", VLC_VAR_ADDRESS);
#endif
590 591 592 593
#ifdef __ANDROID__
    var_Create (mp, "android-jvm", VLC_VAR_ADDRESS);
    var_Create (mp, "drawable-androidwindow", VLC_VAR_ADDRESS);
#endif
594 595 596 597

    var_Create (mp, "keyboard-events", VLC_VAR_BOOL);
    var_SetBool (mp, "keyboard-events", true);
    var_Create (mp, "mouse-events", VLC_VAR_BOOL);
598
    var_SetBool (mp, "mouse-events", true);
599

600
    var_Create (mp, "fullscreen", VLC_VAR_BOOL);
601 602
    var_Create (mp, "autoscale", VLC_VAR_BOOL | VLC_VAR_DOINHERIT);
    var_Create (mp, "zoom", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT);
603 604 605 606
    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);
607

608 609
    var_Create (mp, "vbi-page", VLC_VAR_INTEGER);
    var_SetInteger (mp, "vbi-page", 100);
610

611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628
    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);

629 630
    var_Create (mp, "contrast", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT);
    var_Create (mp, "brightness", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT);
631
    var_Create (mp, "hue", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT);
632 633 634
    var_Create (mp, "saturation", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT);
    var_Create (mp, "gamma", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT);

635
     /* Audio */
636
    var_Create (mp, "aout", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
637
    var_Create (mp, "audio-device", VLC_VAR_STRING);
638
    var_Create (mp, "mute", VLC_VAR_BOOL);
639
    var_Create (mp, "volume", VLC_VAR_FLOAT);
640
    var_Create (mp, "corks", VLC_VAR_INTEGER);
641
    var_Create (mp, "audio-filter", VLC_VAR_STRING);
642 643
    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
644
    var_Create (mp, "amem-cleanup", VLC_VAR_ADDRESS);
645
    var_Create (mp, "amem-play", VLC_VAR_ADDRESS);
646 647 648 649
    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);
650 651 652 653
    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);
654

655 656 657 658 659
    /* 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
660 661
    /* Equalizer */
    var_Create (mp, "equalizer-preamp", VLC_VAR_FLOAT);
662
    var_Create (mp, "equalizer-vlcfreqs", VLC_VAR_BOOL);
Mark Lee's avatar
Mark Lee committed
663 664
    var_Create (mp, "equalizer-bands", VLC_VAR_STRING);

665
    mp->p_md = NULL;
666
    mp->state = libvlc_NothingSpecial;
667
    mp->p_libvlc_instance = instance;
668
    mp->input.p_thread = NULL;
669 670 671 672 673 674
    mp->input.p_resource = input_resource_New(VLC_OBJECT(mp));
    if (unlikely(mp->input.p_resource == NULL))
    {
        vlc_object_release(mp);
        return NULL;
    }
675 676 677 678
    audio_output_t *aout = input_resource_GetAout(mp->input.p_resource);
    if( aout != NULL )
        input_resource_PutAout(mp->input.p_resource, aout);

679
    vlc_mutex_init (&mp->input.lock);
680
    mp->i_refcount = 1;
681 682
    mp->p_event_manager = libvlc_event_manager_new(mp, instance);
    if (unlikely(mp->p_event_manager == NULL))
683
    {
684
        input_resource_Release(mp->input.p_resource);
685
        vlc_object_release(mp);
686 687
        return NULL;
    }
688
    vlc_mutex_init(&mp->object_lock);
689

690 691 692 693 694 695 696 697 698 699
    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);
700
    register_event(mp, SeekableChanged);
701 702 703 704 705 706

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

708
    register_event(mp, Vout);
709
    register_event(mp, ScrambledChanged);
710 711 712
    register_event(mp, ESAdded);
    register_event(mp, ESDeleted);
    register_event(mp, ESSelected);
713 714
    register_event(mp, Corked);
    register_event(mp, Uncorked);
715 716
    register_event(mp, Muted);
    register_event(mp, Unmuted);
717
    register_event(mp, AudioVolume);
718
    register_event(mp, AudioDevice);
719 720

    var_AddCallback(mp, "corks", corks_changed, NULL);
721
    var_AddCallback(mp, "audio-device", audio_device_changed, NULL);
722
    var_AddCallback(mp, "mute", mute_changed, NULL);
723
    var_AddCallback(mp, "volume", volume_changed, NULL);
724

725
    /* Snapshot initialization */
726
    register_event(mp, SnapshotTaken);
Jean-Paul Saman's avatar
Jean-Paul Saman committed
727

728 729
    register_event(mp, MediaChanged);

730
    /* Attach a var callback to the global object to provide the glue between
731 732 733 734 735 736
     * 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.
     */
737
    var_AddCallback(mp->p_libvlc, "snapshot-file", snapshot_was_taken, mp);
738

739
    libvlc_retain(instance);
740
    return mp;
741 742 743
}

/**************************************************************************
JP Dinger's avatar
JP Dinger committed
744
 * Create a Media Instance object with a media descriptor.
745
 **************************************************************************/
746
libvlc_media_player_t *
Rémi Denis-Courmont's avatar
good  
Rémi Denis-Courmont committed
747
libvlc_media_player_new_from_media( libvlc_media_t * p_md )
748
{
749
    libvlc_media_player_t * p_mi;
750

Rémi Denis-Courmont's avatar
good  
Rémi Denis-Courmont committed
751
    p_mi = libvlc_media_player_new( p_md->p_libvlc_instance );
752
    if( !p_mi )
753 754
        return NULL;

755
    libvlc_media_retain( p_md );
756
    p_mi->p_md = p_md;
757 758 759 760 761

    return p_mi;
}

/**************************************************************************
762
 * Destroy a Media Instance object (libvlc internal)
763
 *
JP Dinger's avatar
JP Dinger committed
764
 * Warning: No lock held here, but hey, this is internal. Caller must lock.
765
 **************************************************************************/
766
static void libvlc_media_player_destroy( libvlc_media_player_t *p_mi )
767
{
768
    assert( p_mi );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
769

770
    /* Detach Callback from the main libvlc object */
771
    var_DelCallback( p_mi->p_libvlc,
772
                     "snapshot-file", snapshot_was_taken, p_mi );
773

774
    /* Detach callback from the media player / input manager object */
775
    var_DelCallback( p_mi, "volume", volume_changed, NULL );
776
    var_DelCallback( p_mi, "mute", mute_changed, NULL );
777
    var_DelCallback( p_mi, "audio-device", audio_device_changed, NULL );
778 779
    var_DelCallback( p_mi, "corks", corks_changed, NULL );

780 781
    /* No need for lock_input() because no other threads knows us anymore */
    if( p_mi->input.p_thread )
782
        release_input_thread(p_mi);
783 784
    input_resource_Terminate( p_mi->input.p_resource );
    input_resource_Release( p_mi->input.p_resource );
785
    vlc_mutex_destroy( &p_mi->input.lock );
786

787
    libvlc_event_manager_release( p_mi->p_event_manager );
788
    libvlc_media_release( p_mi->p_md );
789
    vlc_mutex_destroy( &p_mi->object_lock );
790 791

    libvlc_instance_t *instance = p_mi->p_libvlc_instance;
792
    vlc_object_release( p_mi );
793
    libvlc_release(instance);
794 795 796
}

/**************************************************************************
JP Dinger's avatar
JP Dinger committed
797 798 799
 * Release a Media Instance object.
 *
 * Function does the locking.
800
 **************************************************************************/
801
void libvlc_media_player_release( libvlc_media_player_t *p_mi )
802
{
803
    bool destroy;
804

805
    assert( p_mi );
806
    lock(p_mi);
807
    destroy = !--p_mi->i_refcount;
808
    unlock(p_mi);
809

810 811
    if( destroy )
        libvlc_media_player_destroy( p_mi );
812
}
813

814
/**************************************************************************
JP Dinger's avatar
JP Dinger committed
815 816 817
 * Retain a Media Instance object.
 *
 * Caller must hold the lock.
818
 **************************************************************************/
819
void libvlc_media_player_retain( libvlc_media_player_t *p_mi )
820
{
821
    assert( p_mi );
822

823
    lock(p_mi);
824
    p_mi->i_refcount++;
825
    unlock(p_mi);
826
}
827

828
/**************************************************************************
JP Dinger's avatar
JP Dinger committed
829 830 831
 * Set the Media descriptor associated with the instance.
 *
 * Enter without lock -- function will lock the object.
832
 **************************************************************************/
833 834
void libvlc_media_player_set_media(
                            libvlc_media_player_t *p_mi,
835
                            libvlc_media_t *p_md )
836
{
837
    lock_input(p_mi);
838

839
    release_input_thread( p_mi );
840

841
    lock( p_mi );
842
    set_state( p_mi, libvlc_NothingSpecial, true );
843
    unlock_input( p_mi );
844

845
    libvlc_media_release( p_mi->p_md );
846

847 848 849
    if( !p_md )
    {
        p_mi->p_md = NULL;
850
        unlock(p_mi);
851 852 853
        return; /* It is ok to pass a NULL md */
    }

854
    libvlc_media_retain( p_md );
855
    p_mi->p_md = p_md;
856

857 858 859 860
    /* 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;

861
    unlock(p_mi);
862 863 864 865 866 867

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

869 870 871
}

/**************************************************************************
JP Dinger's avatar
JP Dinger committed
872
 * Get the Media descriptor associated with the instance.
873
 **************************************************************************/
874
libvlc_media_t *
875
libvlc_media_player_get_media( libvlc_media_player_t *p_mi )
876
{
877
    libvlc_media_t *p_m;
878

879
    lock( p_mi );
880 881
    p_m = p_mi->p_md;
    if( p_m )
882 883 884 885
        libvlc_media_retain( p_m );
    unlock( p_mi );

    return p_m;
886 887
}

888
/**************************************************************************
JP Dinger's avatar
JP Dinger committed
889
 * Get the event Manager.
890 891
 **************************************************************************/
libvlc_event_manager_t *
892
libvlc_media_player_event_manager( libvlc_media_player_t *p_mi )
893 894 895 896
{
    return p_mi->p_event_manager;
}

897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916
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 );
}

917
/**************************************************************************
JP Dinger's avatar
JP Dinger committed
918
 * Tell media player to start playing.
919
 **************************************************************************/
Rémi Denis-Courmont's avatar
good  
Rémi Denis-Courmont committed
920
int libvlc_media_player_play( libvlc_media_player_t *p_mi )
921
{
922
    lock_input( p_mi );
923

924 925
    input_thread_t *p_input_thread = p_mi->input.p_thread;
    if( p_input_thread )
926
    {
Sam Hocevar's avatar
Sam Hocevar committed
927
        /* A thread already exists, send it a play message */
928
        input_Control( p_input_thread, INPUT_SET_STATE, PLAYING_S );
929
        unlock_input( p_mi );
Rémi Denis-Courmont's avatar
good  
Rémi Denis-Courmont committed
930
        return 0;
931 932
    }

933
    /* Ignore previous exception */
934
    lock(p_mi);
935

936 937
    if( !p_mi->p_md )
    {
938
        unlock(p_mi);
939
        unlock_input( p_mi );
940
        libvlc_printerr( "No associated media descriptor" );
Rémi Denis-Courmont's avatar
good  
Rémi Denis-Courmont committed
941
        return -1;
942 943
    }

944 945 946 947
    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 )
948
    {
949
        unlock_input(p_mi);
Rémi Denis-Courmont's avatar
good  
Rémi Denis-Courmont committed
950 951
        libvlc_printerr( "Not enough memory" );
        return -1;
952
    }
953

954 955
    var_AddCallback( p_input_thread, "can-seek", input_seekable_changed, p_mi );
    var_AddCallback( p_input_thread, "can-pause", input_pausable_changed, p_mi );
956
    var_AddCallback( p_input_thread, "program-scrambled", input_scrambled_changed, p_mi );
Laurent Aimar's avatar
Laurent Aimar committed
957
    var_AddCallback( p_input_thread, "intf-event", input_event_changed, p_mi );
958
    add_es_callbacks( p_input_thread, p_mi );
959

960 961
    if( input_Start( p_input_thread ) )
    {
962
        unlock_input(p_mi);
963
        del_es_callbacks( p_input_thread, p_mi );
964 965
        var_DelCallback( p_input_thread, "intf-event", input_event_changed, p_mi );
        var_DelCallback( p_input_thread, "can-pause", input_pausable_changed, p_mi );
966
        var_DelCallback( p_input_thread, "program-scrambled", input_scrambled_changed, p_mi );
967
        var_DelCallback( p_input_thread, "can-seek", input_seekable_changed, p_mi );
968
        input_Close( p_input_thread );
969 970
        libvlc_printerr( "Input initialization failure" );
        return -1;
971
    }
972 973
    p_mi->input.p_thread = p_input_thread;
    unlock_input(p_mi);
Rémi Denis-Courmont's avatar
good  
Rémi Denis-Courmont committed
974
    return 0;
975 976
}

977
void libvlc_media_player_set_pause( libvlc_media_player_t *p_mi, int paused )
978
{
979
    input_thread_t * p_input_thread = libvlc_get_input_thread( p_mi );
980
    if( !p_input_thread )
981 982
        return;

983
    libvlc_state_t state = libvlc_media_player_get_state( p_mi );
984
    if( state == libvlc_Playing || state == libvlc_Buffering )
985
    {
986 987 988 989 990
        if( paused )
        {
            if( libvlc_media_player_can_pause( p_mi ) )
                input_Control( p_input_thread, INPUT_SET_STATE, PAUSE_S );
            else
991
                input_Stop( p_input_thread );
992
        }
993
    }
994
    else
995 996 997 998
    {
        if( !paused )
            input_Control( p_input_thread, INPUT_SET_STATE, PLAYING_S );
    }
999

1000
    vlc_object_release( p_input_thread );
1001
}
1002

1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013
/**************************************************************************
 * 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 );
}

1014
/**************************************************************************
JP Dinger's avatar
JP Dinger committed
1015 1016 1017
 * Tells whether the media player is currently playing.
 *
 * Enter with lock held.
1018
 **************************************************************************/
1019
int libvlc_media_player_is_playing( libvlc_media_player_t *p_mi )
1020
{
1021
    libvlc_state_t state = libvlc_media_player_get_state( p_mi );
1022
    return (libvlc_Playing == state) || (libvlc_Buffering == state);
1023 1024
}

1025
/**************************************************************************
JP Dinger's avatar
JP Dinger committed
1026
 * Stop playing.
1027
 **************************************************************************/
1028
void libvlc_media_player_stop( libvlc_media_player_t *p_mi )
1029
{
1030
    lock_input(p_mi);
1031
    release_input_thread( p_mi ); /* This will stop the input thread */
1032 1033 1034

    /* Force to go to stopped state, in case we were in Ended, or Error
     * state. */
1035
    if( p_mi->state != libvlc_Stopped )
1036
    {
1037
        set_state( p_mi, libvlc_Stopped, false );
1038 1039 1040

        /* Construct and send the event */
        libvlc_event_t event;
1041
        event.type = libvlc_MediaPlayerStopped;
1042 1043
        libvlc_event_send( p_mi->p_event_manager, &event );
    }
1044

1045
    input_resource_Terminate( p_mi->input.p_resource );
1046
    unlock_input(p_mi);
1047 1048
}

1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059

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 );
1060
    var_SetString( mp, "avcodec-hw", "none" );
1061 1062
    var_SetString( mp, "vout", "vmem" );
    var_SetString( mp, "window", "none" );
1063 1064
}

Rémi Denis-Courmont's avatar