media_list_player.c 27.9 KB
Newer Older
1 2 3
/*****************************************************************************
 * media_list_player.c: libvlc new API media_list player functions
 *****************************************************************************
4
 * Copyright (C) 2007-2015 VLC authors and VideoLAN
5 6 7
 * $Id$
 *
 * Authors: Pierre d'Herbemont <pdherbemont # videolan.org>
8 9
 *          Niles Bindel <zaggal69 # gmail.com>
 *          Rémi Denis-Courmont
10
 *
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
11 12 13
 * 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
14 15 16 17
 * (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
Jean-Baptiste Kempf committed
18 19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
20
 *
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
21 22 23
 * 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.
24
 *****************************************************************************/
25

26 27 28 29
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

30
#include <vlc/libvlc.h>
31
#include <vlc/libvlc_renderer_discoverer.h>
32 33 34 35 36
#include <vlc/libvlc_media.h>
#include <vlc/libvlc_media_list.h>
#include <vlc/libvlc_media_player.h>
#include <vlc/libvlc_media_list_player.h>
#include <vlc/libvlc_events.h>
37
#include <assert.h>
38 39 40 41

#include "libvlc_internal.h"

#include "media_internal.h" // Abuse, could and should be removed
42
#include "media_list_path.h"
43

44
//#define DEBUG_MEDIA_LIST_PLAYER
45

46 47 48 49 50 51 52 53 54 55 56 57
/* This is a very dummy implementation of playlist on top of
 * media_list and media_player.
 *
 * All this code is doing is simply computing the next item
 * of a tree of media_list (see get_next_index()), and play
 * the next item when the current is over. This is happening
 * via the event callback media_player_reached_end().
 *
 * This is thread safe, and we use a two keys (locks) scheme
 * to discriminate between callbacks and regular uses.
 */

58 59
struct libvlc_media_list_player_t
{
60
    libvlc_event_manager_t      event_manager;
61
    int                         i_refcount;
62
    int                         seek_offset;
63
    /* Protect access to this structure. */
64
    vlc_mutex_t                 object_lock;
65 66
    /* Protect access to this structure and from callback execution. */
    vlc_mutex_t                 mp_callback_lock;
67
    vlc_cond_t                  seek_pending;
68 69 70 71
    libvlc_media_list_path_t    current_playing_item_path;
    libvlc_media_t *            p_current_playing_item;
    libvlc_media_list_t *       p_mlist;
    libvlc_media_player_t *     p_mi;
72
    libvlc_playback_mode_t      e_playback_mode;
73 74

    vlc_thread_t                thread;
75 76
};

77 78 79 80 81 82 83 84 85 86
/* This is not yet exported by libvlccore */
static inline void vlc_assert_locked(vlc_mutex_t *mutex)
{
    VLC_UNUSED(mutex);
}

/*
 * Forward declaration
 */

87 88 89
static
int set_relative_playlist_position_and_play(libvlc_media_list_player_t *p_mlp,
                                            int i_relative_position);
90
static void stop(libvlc_media_list_player_t * p_mlp);
91

92 93 94 95 96
/*
 * Private functions
 */

/**************************************************************************
97 98 99 100 101 102
 * Shortcuts
 **************************************************************************/
static inline void lock(libvlc_media_list_player_t * p_mlp)
{
    // Obtain an access to this structure
    vlc_mutex_lock(&p_mlp->object_lock);
103

104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
    // Make sure no callback will occurs at the same time
    vlc_mutex_lock(&p_mlp->mp_callback_lock);
}

static inline void unlock(libvlc_media_list_player_t * p_mlp)
{
    vlc_mutex_unlock(&p_mlp->mp_callback_lock);
    vlc_mutex_unlock(&p_mlp->object_lock);
}

static inline void assert_locked(libvlc_media_list_player_t * p_mlp)
{
    vlc_assert_locked(&p_mlp->mp_callback_lock);
}

static inline libvlc_event_manager_t * mlist_em(libvlc_media_list_player_t * p_mlp)
{
    assert_locked(p_mlp);
122
    return libvlc_media_list_event_manager(p_mlp->p_mlist);
123 124 125 126 127
}

static inline libvlc_event_manager_t * mplayer_em(libvlc_media_list_player_t * p_mlp)
{
    assert_locked(p_mlp);
128
    return libvlc_media_player_event_manager(p_mlp->p_mi);
129 130 131 132
}

/**************************************************************************
 *       get_next_path (private)
133
 *
134
 *  Returns the path to the next item in the list.
135 136
 *  If looping is specified and the current item is the last list item in
 *  the list it will return the first item in the list.
137
 **************************************************************************/
138
static libvlc_media_list_path_t
139
get_next_path(libvlc_media_list_player_t * p_mlp, bool b_loop)
140
{
141 142 143
    assert_locked(p_mlp);

    /* We are entered with libvlc_media_list_lock(p_mlp->p_list) */
144 145 146
    libvlc_media_list_path_t ret;
    libvlc_media_list_t * p_parent_of_playing_item;
    libvlc_media_list_t * p_sublist_of_playing_item;
147

148
    if (!p_mlp->current_playing_item_path)
149
    {
150
        if (!libvlc_media_list_count(p_mlp->p_mlist))
151 152
            return NULL;
        return libvlc_media_list_path_with_root_index(0);
153
    }
154

155 156
    p_sublist_of_playing_item = libvlc_media_list_sublist_at_path(
                            p_mlp->p_mlist,
157
                            p_mlp->current_playing_item_path);
158

159
    /* If item just gained a sublist just play it */
160
    if (p_sublist_of_playing_item)
161
    {
162 163
        libvlc_media_list_release(p_sublist_of_playing_item);
        return libvlc_media_list_path_copy_by_appending(p_mlp->current_playing_item_path, 0);
164
    }
165

166
    /* Try to catch parent element */
167 168
    p_parent_of_playing_item = libvlc_media_list_parentlist_at_path(p_mlp->p_mlist,
                            p_mlp->current_playing_item_path);
169

170 171
    int depth = libvlc_media_list_path_depth(p_mlp->current_playing_item_path);
    if (depth < 1 || !p_parent_of_playing_item)
172 173
        return NULL;

174
    ret = libvlc_media_list_path_copy(p_mlp->current_playing_item_path);
175
    ret[depth - 1]++; /* set to next element */
176

177
    /* If this goes beyond the end of the list */
178
    while(ret[depth-1] >= libvlc_media_list_count(p_parent_of_playing_item))
179
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
180
        depth--;
181
        if (depth <= 0)
182
        {
183 184 185 186 187 188 189 190 191 192 193 194
            if(b_loop)
            {
                ret[0] = 0;
                ret[1] = -1;
                break;
            }
            else
            {
                free(ret);
                libvlc_media_list_release(p_parent_of_playing_item);
                return NULL;
            }
195
        }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
196 197
        ret[depth] = -1;
        ret[depth-1]++;
198 199
        p_parent_of_playing_item  = libvlc_media_list_parentlist_at_path(
                                        p_mlp->p_mlist,
200
                                        ret);
201
    }
202 203 204 205 206 207 208 209 210 211 212

    libvlc_media_list_release(p_parent_of_playing_item);
    return ret;
}

/**************************************************************************
 *       find_last_item (private)
 *
 *  Returns the path of the last descendant of a given item path.
 *  Note: Due to the recursive nature of the function and the need to free
 *        media list paths, paths passed in may be freed if they are replaced.
213
          Recommended usage is to set return value to the same path that was
214 215 216 217 218 219 220 221 222 223
          passed to the function (i.e. item = find_last_item(list, item); )
 **************************************************************************/
static libvlc_media_list_path_t
find_last_item( libvlc_media_list_t * p_mlist, libvlc_media_list_path_t current_item )
{
    libvlc_media_list_t * p_sublist = libvlc_media_list_sublist_at_path(p_mlist, current_item);
    libvlc_media_list_path_t last_item_path = current_item;

    if(p_sublist)
    {
224
        int i_count = libvlc_media_list_count(p_sublist);
225 226 227 228 229 230 231 232 233 234
        if(i_count > 0)
        {
            /* Add the last sublist item to the path. */
            last_item_path = libvlc_media_list_path_copy_by_appending(current_item, i_count - 1);
            free(current_item);
            /* Check that sublist item for more descendants. */
            last_item_path = find_last_item(p_mlist, last_item_path);
        }

        libvlc_media_list_release(p_sublist);
235
    }
236 237 238 239 240 241 242

    return last_item_path;
}

/**************************************************************************
 *       get_previous_path (private)
 *
243
 *  Returns the path to the preceding item in the list.
244 245 246 247 248 249 250 251 252 253 254 255 256 257
 *  If looping is specified and the current item is the first list item in
 *  the list it will return the last descendant of the last item in the list.
 **************************************************************************/
static libvlc_media_list_path_t
get_previous_path(libvlc_media_list_player_t * p_mlp, bool b_loop)
{
    assert_locked(p_mlp);

    /* We are entered with libvlc_media_list_lock(p_mlp->p_list) */
    libvlc_media_list_path_t ret;
    libvlc_media_list_t * p_parent_of_playing_item;

    if (!p_mlp->current_playing_item_path)
    {
258
        if (!libvlc_media_list_count(p_mlp->p_mlist))
259 260 261
            return NULL;
        return libvlc_media_list_path_with_root_index(0);
    }
262

263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
    /* Try to catch parent element */
    p_parent_of_playing_item = libvlc_media_list_parentlist_at_path(
                                            p_mlp->p_mlist,
                                            p_mlp->current_playing_item_path);

    int depth = libvlc_media_list_path_depth(p_mlp->current_playing_item_path);
    if (depth < 1 || !p_parent_of_playing_item)
        return NULL;

    /* Set the return path to the current path */
    ret = libvlc_media_list_path_copy(p_mlp->current_playing_item_path);

    /* Change the return path to the previous list entry */
    ret[depth - 1]--; /* set to previous element */
    ret[depth] = -1;

    /* Is the return path is beyond the start of the current list? */
    if(ret[depth - 1] < 0)
    {
        /* Move to parent of current item */
        depth--;

        /* Are we at the root level of the tree? */
        if (depth <= 0)
        {
            // Is looping enabled?
            if(b_loop)
            {
291
                int i_count = libvlc_media_list_count(p_parent_of_playing_item);
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321

                /* Set current play item to the last element in the list */
                ret[0] = i_count - 1;
                ret[1] = -1;

                /* Set the return path to the last descendant item of the current item */
                ret = find_last_item(p_mlp->p_mlist, ret);
            }
            else
            {
                /* No looping so return empty path. */
                free(ret);
                ret = NULL;
            }
        }
        else
        {
            /* This is the case of moving backward from the beginning of the
            *  subitem list to its parent item.
            *  This ensures that current path is properly terminated to
            *  use that parent.
            */
            ret[depth] = -1;
        }
    }
    else
    {
        ret = find_last_item(p_mlp->p_mlist, ret);
    }

322
    libvlc_media_list_release(p_parent_of_playing_item);
323
    return ret;
324 325
}

326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349
static void *playlist_thread(void *data)
{
    libvlc_media_list_player_t *mlp = data;

    vlc_mutex_lock(&mlp->mp_callback_lock);
    mutex_cleanup_push(&mlp->mp_callback_lock);

    for (;;)
    {
        int canc;

        while (mlp->seek_offset == 0)
            vlc_cond_wait(&mlp->seek_pending, &mlp->mp_callback_lock);

        canc = vlc_savecancel();
        set_relative_playlist_position_and_play(mlp, mlp->seek_offset);
        mlp->seek_offset = 0;
        vlc_restorecancel(canc);
    }

    vlc_cleanup_pop();
    vlc_assert_unreachable();
}

350
/**************************************************************************
351
 *       media_player_reached_end (private) (Event Callback)
352
 **************************************************************************/
353
static void
354
media_player_reached_end(const libvlc_event_t * p_event, void * p_user_data)
355
{
356
    VLC_UNUSED(p_event);
357
    libvlc_media_list_player_t * p_mlp = p_user_data;
358

359 360 361 362
    /* This event is triggered from the input thread, and changing item in
     * the media player requires the input thread to terminate. So we cannot
     * change the playlist state here (it would cause a deadlock). Instead, we
     * defer to a separate thread. Avoiding this would be nice... */
363
    vlc_mutex_lock(&p_mlp->mp_callback_lock);
364 365
    p_mlp->seek_offset++;
    vlc_cond_signal(&p_mlp->seek_pending);
366
    vlc_mutex_unlock(&p_mlp->mp_callback_lock);
367 368 369 370 371
}

/**************************************************************************
 *       playlist_item_deleted (private) (Event Callback)
 **************************************************************************/
372
static void
373
mlist_item_deleted(const libvlc_event_t * p_event, void * p_user_data)
374
{
375
    // Nothing to do. For now.
Rafaël Carré's avatar
Rafaël Carré committed
376
    (void)p_event; (void)p_user_data;
377 378
}

379

380
/**************************************************************************
381
 * install_playlist_observer (private)
382 383
 **************************************************************************/
static void
384
install_playlist_observer(libvlc_media_list_player_t * p_mlp)
385
{
386
    assert_locked(p_mlp);
387
    libvlc_event_attach(mlist_em(p_mlp), libvlc_MediaListItemDeleted, mlist_item_deleted, p_mlp);
388 389 390
}

/**************************************************************************
391
 * uninstall_playlist_observer (private)
392 393
 **************************************************************************/
static void
394
uninstall_playlist_observer(libvlc_media_list_player_t * p_mlp)
395
{
396 397
    assert_locked(p_mlp);
    if (!p_mlp->p_mlist) return;
398
    libvlc_event_detach(mlist_em(p_mlp), libvlc_MediaListItemDeleted, mlist_item_deleted, p_mlp);
399 400 401
}

/**************************************************************************
402
 * install_media_player_observer (private)
403 404
 **************************************************************************/
static void
405
install_media_player_observer(libvlc_media_list_player_t * p_mlp)
406
{
407
    assert_locked(p_mlp);
408
    libvlc_event_attach(mplayer_em(p_mlp), libvlc_MediaPlayerEndReached, media_player_reached_end, p_mlp);
409 410 411 412
}


/**************************************************************************
413
 *       uninstall_media_player_observer (private)
414 415
 **************************************************************************/
static void
416
uninstall_media_player_observer(libvlc_media_list_player_t * p_mlp)
417
{
418 419 420 421 422
    assert_locked(p_mlp);

    // Allow callbacks to run, because detach() will wait until all callbacks are processed.
    // This is safe because only callbacks are allowed, and there execution will be cancelled.
    vlc_mutex_unlock(&p_mlp->mp_callback_lock);
423
    libvlc_event_detach(mplayer_em(p_mlp), libvlc_MediaPlayerEndReached, media_player_reached_end, p_mlp);
424 425 426 427

    // Now, lock back the callback lock. No more callback will be present from this point.
    vlc_mutex_lock(&p_mlp->mp_callback_lock);

428
    // What is here is safe, because we guarantee that we won't be able to anything concurrently,
429
    // - except (cancelled) callbacks - thanks to the object_lock.
430
}
431

432
/**************************************************************************
433
 *       set_current_playing_item (private)
434 435 436 437
 *
 * Playlist lock should be held
 **************************************************************************/
static void
438
set_current_playing_item(libvlc_media_list_player_t * p_mlp, libvlc_media_list_path_t path)
439
{
440
    assert_locked(p_mlp);
441

442 443
    /* First, save the new path that we are going to play */
    if (p_mlp->current_playing_item_path != path)
444
    {
445
        free(p_mlp->current_playing_item_path);
446 447
        p_mlp->current_playing_item_path = path;
    }
448

449 450 451
    if (!path)
        return;

452 453 454
    libvlc_media_t * p_md;
    p_md = libvlc_media_list_item_at_path(p_mlp->p_mlist, path);
    if (!p_md)
455
        return;
456

457 458 459
    /* Make sure media_player_reached_end() won't get called */
    uninstall_media_player_observer(p_mlp);

460
    libvlc_media_player_set_media(p_mlp->p_mi, p_md);
461

462 463
    install_media_player_observer(p_mlp);
    libvlc_media_release(p_md); /* for libvlc_media_list_item_at_index */
464
}
465 466 467 468 469 470

/*
 * Public libvlc functions
 */

/**************************************************************************
471
 *         new (Public)
472 473
 **************************************************************************/
libvlc_media_list_player_t *
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
474
libvlc_media_list_player_new(libvlc_instance_t * p_instance)
475 476
{
    libvlc_media_list_player_t * p_mlp;
Rémi Duraffort's avatar
Rémi Duraffort committed
477
    p_mlp = calloc( 1, sizeof(libvlc_media_list_player_t) );
478 479 480 481 482 483
    if (unlikely(p_mlp == NULL))
    {
        libvlc_printerr("Not enough memory");
        return NULL;
    }

484 485 486 487 488
    p_mlp->i_refcount = 1;
    p_mlp->seek_offset = 0;
    vlc_mutex_init(&p_mlp->object_lock);
    vlc_mutex_init(&p_mlp->mp_callback_lock);
    vlc_cond_init(&p_mlp->seek_pending);
489
    libvlc_event_manager_init(&p_mlp->event_manager, p_mlp);
490

491 492 493
    /* Create the underlying media_player */
    p_mlp->p_mi = libvlc_media_player_new(p_instance);
    if( p_mlp->p_mi == NULL )
494
        goto error;
495
    install_media_player_observer(p_mlp);
496

497 498 499 500 501 502 503
    if (vlc_clone(&p_mlp->thread, playlist_thread, p_mlp,
                  VLC_THREAD_PRIORITY_LOW))
    {
        libvlc_media_player_release(p_mlp->p_mi);
        goto error;
    }

504
    return p_mlp;
505
error:
506
    libvlc_event_manager_destroy(&p_mlp->event_manager);
507 508 509 510 511
    vlc_cond_destroy(&p_mlp->seek_pending);
    vlc_mutex_destroy(&p_mlp->mp_callback_lock);
    vlc_mutex_destroy(&p_mlp->object_lock);
    free(p_mlp);
    return NULL;
512 513 514
}

/**************************************************************************
515
 *         release (Public)
516
 **************************************************************************/
517
void libvlc_media_list_player_release(libvlc_media_list_player_t * p_mlp)
518
{
519
    if (!p_mlp)
520 521
        return;

522
    lock(p_mlp);
523
    p_mlp->i_refcount--;
524
    if (p_mlp->i_refcount > 0)
525
    {
526
        unlock(p_mlp);
527 528
        return;
    }
529
    assert(p_mlp->i_refcount == 0);
530 531 532 533
    unlock(p_mlp);

    vlc_cancel(p_mlp->thread);
    vlc_join(p_mlp->thread, NULL);
534

535
    lock(p_mlp);
536 537
    /* Keep the lock(), because the uninstall functions
     * check for it. That's convenient. */
538 539 540
    uninstall_media_player_observer(p_mlp);
    libvlc_media_player_release(p_mlp->p_mi);

541
    if (p_mlp->p_mlist)
542
    {
543 544
        uninstall_playlist_observer(p_mlp);
        libvlc_media_list_release(p_mlp->p_mlist);
545 546
    }

547 548
    unlock(p_mlp);

549
    libvlc_event_manager_destroy(&p_mlp->event_manager);
550 551 552
    vlc_cond_destroy(&p_mlp->seek_pending);
    vlc_mutex_destroy(&p_mlp->mp_callback_lock);
    vlc_mutex_destroy(&p_mlp->object_lock);
553

554 555
    free(p_mlp->current_playing_item_path);
    free(p_mlp);
556 557
}

558 559 560 561 562 563 564 565 566 567 568 569 570
/**************************************************************************
 *        retain (Public)
 **************************************************************************/
void libvlc_media_list_player_retain(libvlc_media_list_player_t * p_mlp)
{
    if (!p_mlp)
        return;

    lock(p_mlp);
    p_mlp->i_refcount++;
    unlock(p_mlp);
}

571 572 573 574 575 576
/**************************************************************************
 *        event_manager (Public)
 **************************************************************************/
libvlc_event_manager_t *
libvlc_media_list_player_event_manager(libvlc_media_list_player_t * p_mlp)
{
577
    return &p_mlp->event_manager;
578 579
}

580
/**************************************************************************
581
 *        set_media_player (Public)
582
 **************************************************************************/
583
void libvlc_media_list_player_set_media_player(libvlc_media_list_player_t * p_mlp, libvlc_media_player_t * p_mi)
584
{
585
    libvlc_media_player_t *p_oldmi;
586

587
    assert(p_mi != NULL);
588
    libvlc_media_player_retain(p_mi);
589

590 591 592 593
    lock(p_mlp);
    uninstall_media_player_observer(p_mlp);
    p_oldmi = p_mlp->p_mi;
    p_mlp->p_mi = p_mi;
594 595
    install_media_player_observer(p_mlp);
    unlock(p_mlp);
596 597

    libvlc_media_player_release(p_oldmi);
598 599
}

600 601 602 603 604 605 606 607 608
/**************************************************************************
 *        get_media_player (Public)
 **************************************************************************/
libvlc_media_player_t * libvlc_media_list_player_get_media_player(libvlc_media_list_player_t * p_mlp)
{
    libvlc_media_player_retain(p_mlp->p_mi);
    return p_mlp->p_mi;
}

609
/**************************************************************************
610
 *       set_media_list (Public)
611
 **************************************************************************/
612
void libvlc_media_list_player_set_media_list(libvlc_media_list_player_t * p_mlp, libvlc_media_list_t * p_mlist)
613
{
614
    assert (p_mlist);
615

616
    lock(p_mlp);
617
    if (p_mlp->p_mlist)
618
    {
619 620
        uninstall_playlist_observer(p_mlp);
        libvlc_media_list_release(p_mlp->p_mlist);
621
    }
622
    libvlc_media_list_retain(p_mlist);
623
    p_mlp->p_mlist = p_mlist;
624

625
    install_playlist_observer(p_mlp);
626

627
    unlock(p_mlp);
628 629 630 631 632
}

/**************************************************************************
 *        Play (Public)
 **************************************************************************/
633
void libvlc_media_list_player_play(libvlc_media_list_player_t * p_mlp)
634
{
635
    lock(p_mlp);
636
    if (!p_mlp->current_playing_item_path)
637
    {
638
        set_relative_playlist_position_and_play(p_mlp, 1);
639
        unlock(p_mlp);
640
        return; /* Will set to play */
641
    }
642
    libvlc_media_player_play(p_mlp->p_mi);
643
    unlock(p_mlp);
644 645
}

646 647 648 649

/**************************************************************************
 *        Pause (Public)
 **************************************************************************/
650
void libvlc_media_list_player_pause(libvlc_media_list_player_t * p_mlp)
651
{
652
    lock(p_mlp);
653
    libvlc_media_player_pause(p_mlp->p_mi);
654
    unlock(p_mlp);
655 656 657 658 659
}

/**************************************************************************
 *        is_playing (Public)
 **************************************************************************/
660
int
661
libvlc_media_list_player_is_playing(libvlc_media_list_player_t * p_mlp)
662
{
663
    libvlc_state_t state = libvlc_media_player_get_state(p_mlp->p_mi);
664
    return (state == libvlc_Opening) || (state == libvlc_Playing);
665 666 667 668 669
}

/**************************************************************************
 *        State (Public)
 **************************************************************************/
670
libvlc_state_t
671
libvlc_media_list_player_get_state(libvlc_media_list_player_t * p_mlp)
672
{
673
    return libvlc_media_player_get_state(p_mlp->p_mi);
674 675
}

676 677 678
/**************************************************************************
 *        Play item at index (Public)
 **************************************************************************/
679
int libvlc_media_list_player_play_item_at_index(libvlc_media_list_player_t * p_mlp, int i_index)
680
{
681
    lock(p_mlp);
682 683
    libvlc_media_list_path_t path = libvlc_media_list_path_with_root_index(i_index);
    set_current_playing_item(p_mlp, path);
684
    libvlc_media_t *p_md = libvlc_media_player_get_media(p_mlp->p_mi);
685
    libvlc_media_player_play(p_mlp->p_mi);
686
    unlock(p_mlp);
687

688 689 690
    if (!p_md)
        return -1;

691 692 693
    /* Send the next item event */
    libvlc_event_t event;
    event.type = libvlc_MediaListPlayerNextItemSet;
694
    event.u.media_list_player_next_item_set.item = p_md;
695
    libvlc_event_send(&p_mlp->event_manager, &event);
696
    libvlc_media_release(p_md);
697
    return 0;
698 699
}

700 701 702
/**************************************************************************
 *        Play item (Public)
 **************************************************************************/
703
int libvlc_media_list_player_play_item(libvlc_media_list_player_t * p_mlp, libvlc_media_t * p_md)
704
{
705
    lock(p_mlp);
706 707
    libvlc_media_list_path_t path = libvlc_media_list_path_of_item(p_mlp->p_mlist, p_md);
    if (!path)
708
    {
709
        libvlc_printerr("Item not found in media list");
710
        unlock(p_mlp);
711
        return -1;
712
    }
713

714
    set_current_playing_item(p_mlp, path);
715
    libvlc_media_player_play(p_mlp->p_mi);
716
    unlock(p_mlp);
717
    return 0;
718
}
719

720
/**************************************************************************
721 722 723
 *       Stop (Private)
 *
 * Lock must be held.
724
 **************************************************************************/
725
static void stop(libvlc_media_list_player_t * p_mlp)
726
{
727
    assert_locked(p_mlp);
728

729 730 731 732
    /* We are not interested in getting media stop event now */
    uninstall_media_player_observer(p_mlp);
    libvlc_media_player_stop(p_mlp->p_mi);
    install_media_player_observer(p_mlp);
733

734
    free(p_mlp->current_playing_item_path);
735
    p_mlp->current_playing_item_path = NULL;
736 737 738 739

    /* Send the event */
    libvlc_event_t event;
    event.type = libvlc_MediaListPlayerStopped;
740
    libvlc_event_send(&p_mlp->event_manager, &event);
741 742 743 744 745
}

/**************************************************************************
 *       Stop (Public)
 **************************************************************************/
746
void libvlc_media_list_player_stop(libvlc_media_list_player_t * p_mlp)
747 748
{
    lock(p_mlp);
749
    stop(p_mlp);
750
    unlock(p_mlp);
751 752 753
}

/**************************************************************************
754
 *       Set relative playlist position and play (Private)
755
 *
756
 * Sets the currently played item to the given relative play item position
757
 * (based on the currently playing item) and then begins the new item playback.
758
 * Lock must be held.
759
 **************************************************************************/
760
static int set_relative_playlist_position_and_play(
761
                                      libvlc_media_list_player_t * p_mlp,
762
                                      int i_relative_position)
763
{
764
    assert_locked(p_mlp);
765

766
    if (!p_mlp->p_mlist)
767
    {
768
        libvlc_printerr("No media list");
769
        return -1;
770
    }
771

772
    libvlc_media_list_lock(p_mlp->p_mlist);
773

774 775 776 777 778
    libvlc_media_list_path_t path = p_mlp->current_playing_item_path;

    if(p_mlp->e_playback_mode != libvlc_playback_mode_repeat)
    {
        bool b_loop = (p_mlp->e_playback_mode == libvlc_playback_mode_loop);
779

780
        while (i_relative_position > 0)
781
        {
782 783 784
            path = get_next_path(p_mlp, b_loop);
            set_current_playing_item(p_mlp, path);
            --i_relative_position;
785
        }
786 787

        while (i_relative_position < 0)
788
        {
789 790 791
            path = get_previous_path(p_mlp, b_loop);
            set_current_playing_item(p_mlp, path);
            ++i_relative_position;
792 793 794 795 796 797
        }
    }
    else
    {
        set_current_playing_item(p_mlp, path);
    }
798

799 800 801 802
#ifdef DEBUG_MEDIA_LIST_PLAYER
    printf("Playing:");
    libvlc_media_list_path_dump(path);
#endif
803 804

    if (!path)
805
    {
806
        libvlc_media_list_unlock(p_mlp->p_mlist);
807 808 809
        /* Send list played event */
        libvlc_event_t event;
        event.type = libvlc_MediaListPlayerPlayed;
810
        libvlc_event_send(&p_mlp->event_manager, &event);
811
        return -1;
812 813
    }

814
    libvlc_media_player_play(p_mlp->p_mi);
815

816
    libvlc_media_list_unlock(p_mlp->p_mlist);
817 818 819 820

    /* Send the next item event */
    libvlc_event_t event;
    event.type = libvlc_MediaListPlayerNextItemSet;
821 822
    libvlc_media_t * p_md = libvlc_media_list_item_at_path(p_mlp->p_mlist, path);
    event.u.media_list_player_next_item_set.item = p_md;
823
    libvlc_event_send(&p_mlp->event_manager, &event);
824
    libvlc_media_release(p_md);
Pierre's avatar
Pierre committed
825
    return 0;
826
}
827 828 829 830

/**************************************************************************
 *       Next (Public)
 **************************************************************************/
831
int libvlc_media_list_player_next(libvlc_media_list_player_t * p_mlp)
832 833
{
    lock(p_mlp);
834
    int failure = set_relative_playlist_position_and_play(p_mlp, 1);
835
    unlock(p_mlp);
836
    return failure;
837 838 839 840 841
}

/**************************************************************************
 *       Previous (Public)
 **************************************************************************/
842
int libvlc_media_list_player_previous(libvlc_media_list_player_t * p_mlp)
843 844
{
    lock(p_mlp);
845
    int failure = set_relative_playlist_position_and_play(p_mlp, -1);
846
    unlock(p_mlp);
847
    return failure;
848 849
}

850 851 852
/**************************************************************************
 *       Set Playback Mode (Public)
 **************************************************************************/
853
void libvlc_media_list_player_set_playback_mode(
854
                                            libvlc_media_list_player_t * p_mlp,
855
                                            libvlc_playback_mode_t e_mode )
856 857 858 859 860
{
    lock(p_mlp);
    p_mlp->e_playback_mode = e_mode;
    unlock(p_mlp);
}