media_list_player.c 28.1 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
LGPL  
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
LGPL  
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
LGPL  
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 32 33 34 35
#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>
36
#include <assert.h>
37 38 39 40

#include "libvlc_internal.h"

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

43
//#define DEBUG_MEDIA_LIST_PLAYER
44

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

57 58 59 60
struct libvlc_media_list_player_t
{
    libvlc_event_manager_t *    p_event_manager;
    int                         i_refcount;
61
    int                         seek_offset;
62
    /* Protect access to this structure. */
63
    vlc_mutex_t                 object_lock;
64 65
    /* Protect access to this structure and from callback execution. */
    vlc_mutex_t                 mp_callback_lock;
66
    vlc_cond_t                  seek_pending;
67 68 69 70
    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;
71
    libvlc_playback_mode_t      e_playback_mode;
72 73

    vlc_thread_t                thread;
74 75
};

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

/*
 * Forward declaration
 */

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

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

/**************************************************************************
96 97 98 99 100 101
 * 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);
102

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

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

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

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

147
    if (!p_mlp->current_playing_item_path)
148
    {
149
        if (!libvlc_media_list_count(p_mlp->p_mlist))
Pierre d'Herbemont's avatar
Pierre d'Herbemont committed
150 151
            return NULL;
        return libvlc_media_list_path_with_root_index(0);
152
    }
153

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

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

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

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

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

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

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

    return last_item_path;
}

/**************************************************************************
 *       get_previous_path (private)
 *
242
 *  Returns the path to the preceding item in the list.
243 244 245 246 247 248 249 250 251 252 253 254 255 256
 *  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)
    {
257
        if (!libvlc_media_list_count(p_mlp->p_mlist))
258 259 260
            return NULL;
        return libvlc_media_list_path_with_root_index(0);
    }
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 287 288 289
    /* 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)
            {
290
                int i_count = libvlc_media_list_count(p_parent_of_playing_item);
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 318 319 320

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

321
    libvlc_media_list_release(p_parent_of_playing_item);
322
    return ret;
323 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
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();
}

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

358 359 360 361
    /* 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... */
362
    vlc_mutex_lock(&p_mlp->mp_callback_lock);
363 364
    p_mlp->seek_offset++;
    vlc_cond_signal(&p_mlp->seek_pending);
365
    vlc_mutex_unlock(&p_mlp->mp_callback_lock);
366 367 368 369 370
}

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

378

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

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

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


/**************************************************************************
412
 *       uninstall_media_player_observer (private)
413 414
 **************************************************************************/
static void
415
uninstall_media_player_observer(libvlc_media_list_player_t * p_mlp)
416
{
417 418 419 420 421
    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);
422
    libvlc_event_detach(mplayer_em(p_mlp), libvlc_MediaPlayerEndReached, media_player_reached_end, p_mlp);
423 424 425 426

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

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

431
/**************************************************************************
432
 *       set_current_playing_item (private)
433 434 435 436
 *
 * Playlist lock should be held
 **************************************************************************/
static void
437
set_current_playing_item(libvlc_media_list_player_t * p_mlp, libvlc_media_list_path_t path)
438
{
439
    assert_locked(p_mlp);
Pierre d'Herbemont's avatar
Pierre d'Herbemont committed
440

441 442
    /* First, save the new path that we are going to play */
    if (p_mlp->current_playing_item_path != path)
Pierre d'Herbemont's avatar
Pierre d'Herbemont committed
443
    {
444
        free(p_mlp->current_playing_item_path);
Pierre d'Herbemont's avatar
Pierre d'Herbemont committed
445 446
        p_mlp->current_playing_item_path = path;
    }
447

448 449 450
    if (!path)
        return;

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

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

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

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

/*
 * Public libvlc functions
 */

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

483 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
    p_mlp->p_event_manager = libvlc_event_manager_new(p_mlp);
490
    if (unlikely(p_mlp->p_event_manager == NULL))
491
        goto error;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
492

493 494 495 496 497
    /* Create the underlying media_player */
    p_mlp->p_mi = libvlc_media_player_new(p_instance);
    if( p_mlp->p_mi == NULL )
    {
        libvlc_event_manager_release(p_mlp->p_event_manager);
498
        goto error;
499
    }
500
    install_media_player_observer(p_mlp);
501

502 503 504 505 506 507 508 509
    if (vlc_clone(&p_mlp->thread, playlist_thread, p_mlp,
                  VLC_THREAD_PRIORITY_LOW))
    {
        libvlc_media_player_release(p_mlp->p_mi);
        libvlc_event_manager_release(p_mlp->p_event_manager);
        goto error;
    }

510
    return p_mlp;
511 512 513 514 515 516
error:
    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;
517 518 519
}

/**************************************************************************
520
 *         release (Public)
521
 **************************************************************************/
522
void libvlc_media_list_player_release(libvlc_media_list_player_t * p_mlp)
523
{
524
    if (!p_mlp)
525 526
        return;

527
    lock(p_mlp);
528
    p_mlp->i_refcount--;
529
    if (p_mlp->i_refcount > 0)
530
    {
531
        unlock(p_mlp);
532 533
        return;
    }
534
    assert(p_mlp->i_refcount == 0);
535 536 537 538
    unlock(p_mlp);

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

540
    lock(p_mlp);
541 542
    /* Keep the lock(), because the uninstall functions
     * check for it. That's convenient. */
543 544 545
    uninstall_media_player_observer(p_mlp);
    libvlc_media_player_release(p_mlp->p_mi);

546
    if (p_mlp->p_mlist)
547
    {
548 549
        uninstall_playlist_observer(p_mlp);
        libvlc_media_list_release(p_mlp->p_mlist);
550 551
    }

552 553 554
    unlock(p_mlp);

    libvlc_event_manager_release(p_mlp->p_event_manager);
555 556 557
    vlc_cond_destroy(&p_mlp->seek_pending);
    vlc_mutex_destroy(&p_mlp->mp_callback_lock);
    vlc_mutex_destroy(&p_mlp->object_lock);
558

559 560
    free(p_mlp->current_playing_item_path);
    free(p_mlp);
561 562
}

563 564 565 566 567 568 569 570 571 572 573 574 575
/**************************************************************************
 *        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);
}

576 577 578 579 580 581 582 583 584
/**************************************************************************
 *        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;
}

585
/**************************************************************************
586
 *        set_media_player (Public)
587
 **************************************************************************/
588
void libvlc_media_list_player_set_media_player(libvlc_media_list_player_t * p_mlp, libvlc_media_player_t * p_mi)
589
{
590
    libvlc_media_player_t *p_oldmi;
591

592
    assert(p_mi != NULL);
593
    libvlc_media_player_retain(p_mi);
594

595 596 597 598
    lock(p_mlp);
    uninstall_media_player_observer(p_mlp);
    p_oldmi = p_mlp->p_mi;
    p_mlp->p_mi = p_mi;
599 600
    install_media_player_observer(p_mlp);
    unlock(p_mlp);
601 602

    libvlc_media_player_release(p_oldmi);
603 604
}

605 606 607 608 609 610 611 612 613
/**************************************************************************
 *        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;
}

614
/**************************************************************************
615
 *       set_media_list (Public)
616
 **************************************************************************/
617
void libvlc_media_list_player_set_media_list(libvlc_media_list_player_t * p_mlp, libvlc_media_list_t * p_mlist)
618
{
619
    assert (p_mlist);
Jean-Paul Saman's avatar
Jean-Paul Saman committed
620

621
    lock(p_mlp);
622
    if (p_mlp->p_mlist)
623
    {
624 625
        uninstall_playlist_observer(p_mlp);
        libvlc_media_list_release(p_mlp->p_mlist);
626
    }
627
    libvlc_media_list_retain(p_mlist);
628
    p_mlp->p_mlist = p_mlist;
629

630
    install_playlist_observer(p_mlp);
631

632
    unlock(p_mlp);
633 634 635 636 637
}

/**************************************************************************
 *        Play (Public)
 **************************************************************************/
638
void libvlc_media_list_player_play(libvlc_media_list_player_t * p_mlp)
639
{
640
    lock(p_mlp);
641
    if (!p_mlp->current_playing_item_path)
642
    {
643
        set_relative_playlist_position_and_play(p_mlp, 1);
644
        unlock(p_mlp);
645
        return; /* Will set to play */
646
    }
647
    libvlc_media_player_play(p_mlp->p_mi);
648
    unlock(p_mlp);
649 650
}

651 652 653 654

/**************************************************************************
 *        Pause (Public)
 **************************************************************************/
655
void libvlc_media_list_player_pause(libvlc_media_list_player_t * p_mlp)
656
{
657
    lock(p_mlp);
658
    libvlc_media_player_pause(p_mlp->p_mi);
659
    unlock(p_mlp);
660 661 662 663 664
}

/**************************************************************************
 *        is_playing (Public)
 **************************************************************************/
665
int
666
libvlc_media_list_player_is_playing(libvlc_media_list_player_t * p_mlp)
667
{
668
    libvlc_state_t state = libvlc_media_player_get_state(p_mlp->p_mi);
669 670 671 672 673 674 675
    return (state == libvlc_Opening) || (state == libvlc_Buffering) ||
           (state == libvlc_Playing);
}

/**************************************************************************
 *        State (Public)
 **************************************************************************/
676
libvlc_state_t
677
libvlc_media_list_player_get_state(libvlc_media_list_player_t * p_mlp)
678
{
679
    return libvlc_media_player_get_state(p_mlp->p_mi);
680 681
}

682 683 684
/**************************************************************************
 *        Play item at index (Public)
 **************************************************************************/
685
int libvlc_media_list_player_play_item_at_index(libvlc_media_list_player_t * p_mlp, int i_index)
686
{
687
    lock(p_mlp);
688 689
    libvlc_media_list_path_t path = libvlc_media_list_path_with_root_index(i_index);
    set_current_playing_item(p_mlp, path);
690
    libvlc_media_player_play(p_mlp->p_mi);
691
    unlock(p_mlp);
692

693 694 695
    /* Send the next item event */
    libvlc_event_t event;
    event.type = libvlc_MediaListPlayerNextItemSet;
696 697
    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;
698
    libvlc_event_send(p_mlp->p_event_manager, &event);
699
    libvlc_media_release(p_md);
700
    return 0;
701 702
}

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

717
    set_current_playing_item(p_mlp, path);
718
    libvlc_media_player_play(p_mlp->p_mi);
719
    unlock(p_mlp);
720
    return 0;
721
}
722

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

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
732 733 734 735
    /* 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);
736

737
    free(p_mlp->current_playing_item_path);
738
    p_mlp->current_playing_item_path = NULL;
739 740 741 742 743

    /* Send the event */
    libvlc_event_t event;
    event.type = libvlc_MediaListPlayerStopped;
    libvlc_event_send(p_mlp->p_event_manager, &event);
744 745 746 747 748
}

/**************************************************************************
 *       Stop (Public)
 **************************************************************************/
749
void libvlc_media_list_player_stop(libvlc_media_list_player_t * p_mlp)
750 751
{
    lock(p_mlp);
752
    stop(p_mlp);
753
    unlock(p_mlp);
754 755 756
}

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

769
    if (!p_mlp->p_mlist)
770
    {
771
        libvlc_printerr("No media list");
772
        return -1;
773
    }
774

775
    libvlc_media_list_lock(p_mlp->p_mlist);
776

777 778 779 780 781
    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);
782

783
        while (i_relative_position > 0)
784
        {
785 786 787
            path = get_next_path(p_mlp, b_loop);
            set_current_playing_item(p_mlp, path);
            --i_relative_position;
788
        }
789 790

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

802 803 804 805
#ifdef DEBUG_MEDIA_LIST_PLAYER
    printf("Playing:");
    libvlc_media_list_path_dump(path);
#endif
806 807

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

817
    libvlc_media_player_play(p_mlp->p_mi);
818

819
    libvlc_media_list_unlock(p_mlp->p_mlist);
820 821 822 823

    /* Send the next item event */
    libvlc_event_t event;
    event.type = libvlc_MediaListPlayerNextItemSet;
824 825
    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;
826
    libvlc_event_send(p_mlp->p_event_manager, &event);
827
    libvlc_media_release(p_md);
Pierre's avatar
Pierre committed
828
    return 0;
829
}
830 831 832 833

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

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

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