media_list_player.c 26.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/*****************************************************************************
 * media_list_player.c: libvlc new API media_list player functions
 *****************************************************************************
 * Copyright (C) 2007 the VideoLAN team
 * $Id$
 *
 * Authors: Pierre d'Herbemont <pdherbemont # videolan.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/
23

24 25 26 27
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

28
#include <vlc/libvlc.h>
29 30 31 32 33
#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>
34
#include <assert.h>
35 36 37 38

#include "libvlc_internal.h"

#include "media_internal.h" // Abuse, could and should be removed
39
#include "media_list_path.h"
40

41
//#define DEBUG_MEDIA_LIST_PLAYER
42

43 44 45 46 47 48 49 50 51 52 53 54
/* 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.
 */

55 56 57 58 59
struct libvlc_media_list_player_t
{
    libvlc_event_manager_t *    p_event_manager;
    libvlc_instance_t *         p_libvlc_instance;
    int                         i_refcount;
60
    /* Protect access to this structure. */
61
    vlc_mutex_t                 object_lock;
62 63 64 65
    /* Protect access to this structure and from callback execution. */
    vlc_mutex_t                 mp_callback_lock;
    /* Indicate to media player callbacks that they are cancelled. */
    bool                        are_mp_callback_cancelled;
66 67 68 69
    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;
70
    libvlc_playback_mode_t      e_playback_mode;
71 72
};

73 74 75 76 77 78 79 80 81 82
/* This is not yet exported by libvlccore */
static inline void vlc_assert_locked(vlc_mutex_t *mutex)
{
    VLC_UNUSED(mutex);
}

/*
 * Forward declaration
 */

83 84 85
static
int set_relative_playlist_position_and_play(libvlc_media_list_player_t *p_mlp,
                                            int i_relative_position);
86
static void stop(libvlc_media_list_player_t * p_mlp);
87

88 89 90 91 92
/*
 * Private functions
 */

/**************************************************************************
93 94 95 96 97 98
 * 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);
99

100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
    // 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);
118
    return libvlc_media_list_event_manager(p_mlp->p_mlist);
119 120 121 122 123
}

static inline libvlc_event_manager_t * mplayer_em(libvlc_media_list_player_t * p_mlp)
{
    assert_locked(p_mlp);
124
    return libvlc_media_player_event_manager(p_mlp->p_mi);
125 126 127 128
}

/**************************************************************************
 *       get_next_path (private)
129
 *
130
 *  Returns the path to the next item in the list.
131 132
 *  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.
133
 **************************************************************************/
134
static libvlc_media_list_path_t
135
get_next_path(libvlc_media_list_player_t * p_mlp, bool b_loop)
136
{
137 138 139
    assert_locked(p_mlp);

    /* We are entered with libvlc_media_list_lock(p_mlp->p_list) */
140 141 142
    libvlc_media_list_path_t ret;
    libvlc_media_list_t * p_parent_of_playing_item;
    libvlc_media_list_t * p_sublist_of_playing_item;
143

144
    if (!p_mlp->current_playing_item_path)
145
    {
146
        if (!libvlc_media_list_count(p_mlp->p_mlist))
147 148
            return NULL;
        return libvlc_media_list_path_with_root_index(0);
149
    }
150

151 152
    p_sublist_of_playing_item = libvlc_media_list_sublist_at_path(
                            p_mlp->p_mlist,
153
                            p_mlp->current_playing_item_path);
154

155
    /* If item just gained a sublist just play it */
156
    if (p_sublist_of_playing_item)
157
    {
158 159
        libvlc_media_list_release(p_sublist_of_playing_item);
        return libvlc_media_list_path_copy_by_appending(p_mlp->current_playing_item_path, 0);
160
    }
161

162
    /* Try to catch parent element */
163 164
    p_parent_of_playing_item = libvlc_media_list_parentlist_at_path(p_mlp->p_mlist,
                            p_mlp->current_playing_item_path);
165

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

170
    ret = libvlc_media_list_path_copy(p_mlp->current_playing_item_path);
171
    ret[depth - 1]++; /* set to next element */
172

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

    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.
209
          Recommended usage is to set return value to the same path that was
210 211 212 213 214 215 216 217 218 219
          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)
    {
220
        int i_count = libvlc_media_list_count(p_sublist);
221 222 223 224 225 226 227 228 229 230
        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);
231
    }
232 233 234 235 236 237 238

    return last_item_path;
}

/**************************************************************************
 *       get_previous_path (private)
 *
239
 *  Returns the path to the preceding item in the list.
240 241 242 243 244 245 246 247 248 249 250 251 252 253
 *  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)
    {
254
        if (!libvlc_media_list_count(p_mlp->p_mlist))
255 256 257
            return NULL;
        return libvlc_media_list_path_with_root_index(0);
    }
258

259 260 261 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
    /* 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)
            {
287
                int i_count = libvlc_media_list_count(p_parent_of_playing_item);
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 313 314 315 316 317

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

318
    libvlc_media_list_release(p_parent_of_playing_item);
319
    return ret;
320 321 322
}

/**************************************************************************
323
 *       media_player_reached_end (private) (Event Callback)
324
 **************************************************************************/
325
static void
326
media_player_reached_end(const libvlc_event_t * p_event, void * p_user_data)
327
{
328
    VLC_UNUSED(p_event);
329
    libvlc_media_list_player_t * p_mlp = p_user_data;
330 331 332

    vlc_mutex_lock(&p_mlp->mp_callback_lock);
    if (!p_mlp->are_mp_callback_cancelled)
333
        set_relative_playlist_position_and_play(p_mlp, 1);
334
    vlc_mutex_unlock(&p_mlp->mp_callback_lock);
335 336 337 338 339
}

/**************************************************************************
 *       playlist_item_deleted (private) (Event Callback)
 **************************************************************************/
340
static void
341
mlist_item_deleted(const libvlc_event_t * p_event, void * p_user_data)
342
{
343
    // Nothing to do. For now.
Rafaël Carré's avatar
Rafaël Carré committed
344
    (void)p_event; (void)p_user_data;
345 346
}

347

348
/**************************************************************************
349
 * install_playlist_observer (private)
350 351
 **************************************************************************/
static void
352
install_playlist_observer(libvlc_media_list_player_t * p_mlp)
353
{
354
    assert_locked(p_mlp);
355
    libvlc_event_attach(mlist_em(p_mlp), libvlc_MediaListItemDeleted, mlist_item_deleted, p_mlp);
356 357 358
}

/**************************************************************************
359
 * uninstall_playlist_observer (private)
360 361
 **************************************************************************/
static void
362
uninstall_playlist_observer(libvlc_media_list_player_t * p_mlp)
363
{
364 365
    assert_locked(p_mlp);
    if (!p_mlp->p_mlist) return;
366
    libvlc_event_detach(mlist_em(p_mlp), libvlc_MediaListItemDeleted, mlist_item_deleted, p_mlp);
367 368 369
}

/**************************************************************************
370
 * install_media_player_observer (private)
371 372
 **************************************************************************/
static void
373
install_media_player_observer(libvlc_media_list_player_t * p_mlp)
374
{
375
    assert_locked(p_mlp);
376
    libvlc_event_attach_async(mplayer_em(p_mlp), libvlc_MediaPlayerEndReached, media_player_reached_end, p_mlp);
377 378 379 380
}


/**************************************************************************
381
 *       uninstall_media_player_observer (private)
382 383
 **************************************************************************/
static void
384
uninstall_media_player_observer(libvlc_media_list_player_t * p_mlp)
385
{
386 387 388 389 390
    assert_locked(p_mlp);
    if (!p_mlp->p_mi) return;

    // From now on, media_player callback won't be relevant.
    p_mlp->are_mp_callback_cancelled = true;
391

392 393 394
    // 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);
395
    libvlc_event_detach(mplayer_em(p_mlp), libvlc_MediaPlayerEndReached, media_player_reached_end, p_mlp);
396 397 398

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

401
    // What is here is safe, because we guarantee that we won't be able to anything concurrently,
402
    // - except (cancelled) callbacks - thanks to the object_lock.
403
}
404

405
/**************************************************************************
406
 *       set_current_playing_item (private)
407 408 409 410
 *
 * Playlist lock should be held
 **************************************************************************/
static void
411
set_current_playing_item(libvlc_media_list_player_t * p_mlp, libvlc_media_list_path_t path)
412
{
413
    assert_locked(p_mlp);
414

415 416
    /* First, save the new path that we are going to play */
    if (p_mlp->current_playing_item_path != path)
417
    {
418
        free(p_mlp->current_playing_item_path);
419 420
        p_mlp->current_playing_item_path = path;
    }
421

422 423 424
    if (!path)
        return;

425 426 427
    libvlc_media_t * p_md;
    p_md = libvlc_media_list_item_at_path(p_mlp->p_mlist, path);
    if (!p_md)
428
        return;
429

430 431 432 433 434
    /* Make sure media_player_reached_end() won't get called */
    uninstall_media_player_observer(p_mlp);

    /* Create a new media_player if there is none */
    if (!p_mlp->p_mi)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
435
        p_mlp->p_mi = libvlc_media_player_new_from_media(p_md);
436

437
    libvlc_media_player_set_media(p_mlp->p_mi, p_md);
438

439 440
    install_media_player_observer(p_mlp);
    libvlc_media_release(p_md); /* for libvlc_media_list_item_at_index */
441
}
442 443 444 445 446 447

/*
 * Public libvlc functions
 */

/**************************************************************************
448
 *         new (Public)
449 450
 **************************************************************************/
libvlc_media_list_player_t *
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
451
libvlc_media_list_player_new(libvlc_instance_t * p_instance)
452 453
{
    libvlc_media_list_player_t * p_mlp;
Rémi Duraffort's avatar
Rémi Duraffort committed
454
    p_mlp = calloc( 1, sizeof(libvlc_media_list_player_t) );
455 456 457 458 459 460 461 462 463 464
    if (unlikely(p_mlp == NULL))
    {
        libvlc_printerr("Not enough memory");
        return NULL;
    }

    p_mlp->p_event_manager = libvlc_event_manager_new(p_mlp, p_instance);
    if (unlikely(p_mlp->p_event_manager == NULL))
    {
        free (p_mlp);
465
        return NULL;
466
    }
467

468
    libvlc_retain(p_instance);
469
    p_mlp->p_libvlc_instance = p_instance;
470 471 472
    p_mlp->i_refcount = 1;
    vlc_mutex_init(&p_mlp->object_lock);
    vlc_mutex_init(&p_mlp->mp_callback_lock);
473
    libvlc_event_manager_register_event_type(p_mlp->p_event_manager, libvlc_MediaListPlayerNextItemSet);
474
    p_mlp->e_playback_mode = libvlc_playback_mode_default;
475

476
    return p_mlp;
477 478 479
}

/**************************************************************************
480
 *         release (Public)
481
 **************************************************************************/
482
void libvlc_media_list_player_release(libvlc_media_list_player_t * p_mlp)
483
{
484
    if (!p_mlp)
485 486
        return;

487
    lock(p_mlp);
488
    p_mlp->i_refcount--;
489
    if (p_mlp->i_refcount > 0)
490
    {
491
        unlock(p_mlp);
492 493 494
        return;
    }

495 496 497 498
    assert(p_mlp->i_refcount == 0);

    /* Keep the lock(), because the uninstall functions
     * check for it. That's convenient. */
499

500 501 502 503
    if (p_mlp->p_mi)
    {
        uninstall_media_player_observer(p_mlp);
        libvlc_media_player_release(p_mlp->p_mi);
504
    }
505
    if (p_mlp->p_mlist)
506
    {
507 508
        uninstall_playlist_observer(p_mlp);
        libvlc_media_list_release(p_mlp->p_mlist);
509 510
    }

511 512 513 514 515
    unlock(p_mlp);
    vlc_mutex_destroy(&p_mlp->object_lock);
    vlc_mutex_destroy(&p_mlp->mp_callback_lock);

    libvlc_event_manager_release(p_mlp->p_event_manager);
516

517 518 519
    free(p_mlp->current_playing_item_path);
    libvlc_release(p_mlp->p_libvlc_instance);
    free(p_mlp);
520 521
}

522 523 524 525 526 527 528 529 530 531 532 533 534
/**************************************************************************
 *        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);
}

535 536 537 538 539 540 541 542 543
/**************************************************************************
 *        event_manager (Public)
 **************************************************************************/
libvlc_event_manager_t *
libvlc_media_list_player_event_manager(libvlc_media_list_player_t * p_mlp)
{
    return p_mlp->p_event_manager;
}

544
/**************************************************************************
545
 *        set_media_player (Public)
546
 **************************************************************************/
547
void libvlc_media_list_player_set_media_player(libvlc_media_list_player_t * p_mlp, libvlc_media_player_t * p_mi)
548
{
549
    lock(p_mlp);
550

551
    if (p_mlp->p_mi)
552
    {
553 554
        uninstall_media_player_observer(p_mlp);
        libvlc_media_player_release(p_mlp->p_mi);
555
    }
556
    libvlc_media_player_retain(p_mi);
557
    p_mlp->p_mi = p_mi;
558

559
    install_media_player_observer(p_mlp);
560

561
    unlock(p_mlp);
562 563 564
}

/**************************************************************************
565
 *       set_media_list (Public)
566
 **************************************************************************/
567
void libvlc_media_list_player_set_media_list(libvlc_media_list_player_t * p_mlp, libvlc_media_list_t * p_mlist)
568
{
569
    assert (p_mlist);
570

571
    lock(p_mlp);
572
    if (p_mlp->p_mlist)
573
    {
574 575
        uninstall_playlist_observer(p_mlp);
        libvlc_media_list_release(p_mlp->p_mlist);
576
    }
577
    libvlc_media_list_retain(p_mlist);
578
    p_mlp->p_mlist = p_mlist;
579

580
    install_playlist_observer(p_mlp);
581

582
    unlock(p_mlp);
583 584 585 586 587
}

/**************************************************************************
 *        Play (Public)
 **************************************************************************/
588
void libvlc_media_list_player_play(libvlc_media_list_player_t * p_mlp)
589
{
590
    lock(p_mlp);
591
    if (!p_mlp->current_playing_item_path)
592
    {
593
        set_relative_playlist_position_and_play(p_mlp, 1);
594
        unlock(p_mlp);
595
        return; /* Will set to play */
596
    }
597
    libvlc_media_player_play(p_mlp->p_mi);
598
    unlock(p_mlp);
599 600
}

601 602 603 604

/**************************************************************************
 *        Pause (Public)
 **************************************************************************/
605
void libvlc_media_list_player_pause(libvlc_media_list_player_t * p_mlp)
606
{
607
    lock(p_mlp);
608
    if (!p_mlp->p_mi)
609 610
    {
        unlock(p_mlp);
611
        return;
612
    }
613
    libvlc_media_player_pause(p_mlp->p_mi);
614
    unlock(p_mlp);
615 616 617 618 619
}

/**************************************************************************
 *        is_playing (Public)
 **************************************************************************/
620
int
621
libvlc_media_list_player_is_playing(libvlc_media_list_player_t * p_mlp)
622
{
623 624 625 626
    if (!p_mlp->p_mi)
    {
        return libvlc_NothingSpecial;
    }
627
    libvlc_state_t state = libvlc_media_player_get_state(p_mlp->p_mi);
628 629 630 631 632 633 634
    return (state == libvlc_Opening) || (state == libvlc_Buffering) ||
           (state == libvlc_Playing);
}

/**************************************************************************
 *        State (Public)
 **************************************************************************/
635
libvlc_state_t
636
libvlc_media_list_player_get_state(libvlc_media_list_player_t * p_mlp)
637
{
638
    if (!p_mlp->p_mi)
639
        return libvlc_Ended;
640
    return libvlc_media_player_get_state(p_mlp->p_mi);
641 642
}

643 644 645
/**************************************************************************
 *        Play item at index (Public)
 **************************************************************************/
646
int libvlc_media_list_player_play_item_at_index(libvlc_media_list_player_t * p_mlp, int i_index)
647
{
648
    lock(p_mlp);
649
    set_current_playing_item(p_mlp, libvlc_media_list_path_with_root_index(i_index));
650
    libvlc_media_player_play(p_mlp->p_mi);
651
    unlock(p_mlp);
652

653 654 655
    /* Send the next item event */
    libvlc_event_t event;
    event.type = libvlc_MediaListPlayerNextItemSet;
656
    libvlc_event_send(p_mlp->p_event_manager, &event);
657
    return 0;
658 659
}

660 661 662
/**************************************************************************
 *        Play item (Public)
 **************************************************************************/
663
int libvlc_media_list_player_play_item(libvlc_media_list_player_t * p_mlp, libvlc_media_t * p_md)
664
{
665
    lock(p_mlp);
666 667
    libvlc_media_list_path_t path = libvlc_media_list_path_of_item(p_mlp->p_mlist, p_md);
    if (!path)
668
    {
669
        libvlc_printerr("Item not found in media list");
670
        unlock(p_mlp);
671
        return -1;
672
    }
673

674
    set_current_playing_item(p_mlp, path);
675
    libvlc_media_player_play(p_mlp->p_mi);
676
    unlock(p_mlp);
677
    return 0;
678
}
679

680
/**************************************************************************
681 682 683
 *       Stop (Private)
 *
 * Lock must be held.
684
 **************************************************************************/
685
static void stop(libvlc_media_list_player_t * p_mlp)
686
{
687
    assert_locked(p_mlp);
688

689
    if (p_mlp->p_mi)
690
    {
691
        /* We are not interested in getting media stop event now */
692
        uninstall_media_player_observer(p_mlp);
693
        libvlc_media_player_stop(p_mlp->p_mi);
694
        install_media_player_observer(p_mlp);
695
    }
696

697
    free(p_mlp->current_playing_item_path);
698
    p_mlp->current_playing_item_path = NULL;
699 700 701 702 703
}

/**************************************************************************
 *       Stop (Public)
 **************************************************************************/
704
void libvlc_media_list_player_stop(libvlc_media_list_player_t * p_mlp)
705 706
{
    lock(p_mlp);
707
    stop(p_mlp);
708
    unlock(p_mlp);
709 710 711
}

/**************************************************************************
712
 *       Set relative playlist position and play (Private)
713
 *
714
 * Sets the currently played item to the given relative play item position
715
 * (based on the currently playing item) and then begins the new item playback.
716
 * Lock must be held.
717
 **************************************************************************/
718
static int set_relative_playlist_position_and_play(
719
                                      libvlc_media_list_player_t * p_mlp,
720
                                      int i_relative_position)
721
{
722
    assert_locked(p_mlp);
723

724
    if (!p_mlp->p_mlist)
725
    {
726
        libvlc_printerr("No media list");
727
        return -1;
728
    }
729

730
    libvlc_media_list_lock(p_mlp->p_mlist);
731

732 733 734 735 736
    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);
737

738 739 740 741 742 743 744 745 746 747 748 749
        if(i_relative_position > 0)
        {
            do
            {
                path = get_next_path(p_mlp, b_loop);
                set_current_playing_item(p_mlp, path);
                --i_relative_position;
            }
            while(i_relative_position > 0);
        }
        else if(i_relative_position < 0)
        {
750
            do
751 752 753 754
            {
                path = get_previous_path(p_mlp, b_loop);
                set_current_playing_item(p_mlp, path);
                ++i_relative_position;
755
            }
756 757 758 759 760 761 762
            while (i_relative_position < 0);
        }
    }
    else
    {
        set_current_playing_item(p_mlp, path);
    }
763

764 765 766 767
#ifdef DEBUG_MEDIA_LIST_PLAYER
    printf("Playing:");
    libvlc_media_list_path_dump(path);
#endif
768 769

    if (!path)
770
    {
771
        libvlc_media_list_unlock(p_mlp->p_mlist);
772
        return -1;
773 774
    }

775
    libvlc_media_player_play(p_mlp->p_mi);
776

777
    libvlc_media_list_unlock(p_mlp->p_mlist);
778 779 780 781

    /* Send the next item event */
    libvlc_event_t event;
    event.type = libvlc_MediaListPlayerNextItemSet;
782 783
    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;
784
    libvlc_event_send(p_mlp->p_event_manager, &event);
785
    libvlc_media_release(p_md);
Pierre's avatar
Pierre committed
786
    return 0;
787
}
788 789 790 791

/**************************************************************************
 *       Next (Public)
 **************************************************************************/
792
int libvlc_media_list_player_next(libvlc_media_list_player_t * p_mlp)
793 794
{
    lock(p_mlp);
795
    int failure = set_relative_playlist_position_and_play(p_mlp, 1);
796
    unlock(p_mlp);
797
    return failure;
798 799 800 801 802
}

/**************************************************************************
 *       Previous (Public)
 **************************************************************************/
803
int libvlc_media_list_player_previous(libvlc_media_list_player_t * p_mlp)
804 805
{
    lock(p_mlp);
806
    int failure = set_relative_playlist_position_and_play(p_mlp, -1);
807
    unlock(p_mlp);
808
    return failure;
809 810
}

811 812 813
/**************************************************************************
 *       Set Playback Mode (Public)
 **************************************************************************/
814
void libvlc_media_list_player_set_playback_mode(
815
                                            libvlc_media_list_player_t * p_mlp,
816
                                            libvlc_playback_mode_t e_mode )
817 818 819 820 821
{
    lock(p_mlp);
    p_mlp->e_playback_mode = e_mode;
    unlock(p_mlp);
}