LGPL
[vlc.git] / lib / media_list_player.c
1 /*****************************************************************************
2  * media_list_player.c: libvlc new API media_list player functions
3  *****************************************************************************
4  * Copyright (C) 2007 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Pierre d'Herbemont <pdherbemont # videolan.org>
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 #include <vlc/libvlc.h>
29 #include <vlc/libvlc_media.h>
30 #include <vlc/libvlc_media_list.h>
31 #include <vlc/libvlc_media_player.h>
32 #include <vlc/libvlc_media_list_player.h>
33 #include <vlc/libvlc_events.h>
34 #include <assert.h>
35
36 #include "libvlc_internal.h"
37
38 #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 /* This is a very dummy implementation of playlist on top of
44  * media_list and media_player.
45  *
46  * All this code is doing is simply computing the next item
47  * of a tree of media_list (see get_next_index()), and play
48  * the next item when the current is over. This is happening
49  * via the event callback media_player_reached_end().
50  *
51  * This is thread safe, and we use a two keys (locks) scheme
52  * to discriminate between callbacks and regular uses.
53  */
54
55 struct libvlc_media_list_player_t
56 {
57     libvlc_event_manager_t *    p_event_manager;
58     libvlc_instance_t *         p_libvlc_instance;
59     int                         i_refcount;
60     /* Protect access to this structure. */
61     vlc_mutex_t                 object_lock;
62     /* Protect access to this structure and from callback execution. */
63     vlc_mutex_t                 mp_callback_lock;
64     /* Indicate to media player callbacks that they are cancelled. */
65     bool                        are_mp_callback_cancelled;
66     libvlc_media_list_path_t    current_playing_item_path;
67     libvlc_media_t *            p_current_playing_item;
68     libvlc_media_list_t *       p_mlist;
69     libvlc_media_player_t *     p_mi;
70     libvlc_playback_mode_t      e_playback_mode;
71 };
72
73 /* This is not yet exported by libvlccore */
74 static inline void vlc_assert_locked(vlc_mutex_t *mutex)
75 {
76     VLC_UNUSED(mutex);
77 }
78
79 /*
80  * Forward declaration
81  */
82
83 static
84 int set_relative_playlist_position_and_play(libvlc_media_list_player_t *p_mlp,
85                                             int i_relative_position);
86 static void stop(libvlc_media_list_player_t * p_mlp);
87
88 /*
89  * Private functions
90  */
91
92 /**************************************************************************
93  * Shortcuts
94  **************************************************************************/
95 static inline void lock(libvlc_media_list_player_t * p_mlp)
96 {
97     // Obtain an access to this structure
98     vlc_mutex_lock(&p_mlp->object_lock);
99
100     // Make sure no callback will occurs at the same time
101     vlc_mutex_lock(&p_mlp->mp_callback_lock);
102 }
103
104 static inline void unlock(libvlc_media_list_player_t * p_mlp)
105 {
106     vlc_mutex_unlock(&p_mlp->mp_callback_lock);
107     vlc_mutex_unlock(&p_mlp->object_lock);
108 }
109
110 static inline void assert_locked(libvlc_media_list_player_t * p_mlp)
111 {
112     vlc_assert_locked(&p_mlp->mp_callback_lock);
113 }
114
115 static inline libvlc_event_manager_t * mlist_em(libvlc_media_list_player_t * p_mlp)
116 {
117     assert_locked(p_mlp);
118     return libvlc_media_list_event_manager(p_mlp->p_mlist);
119 }
120
121 static inline libvlc_event_manager_t * mplayer_em(libvlc_media_list_player_t * p_mlp)
122 {
123     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  *  If looping is specified and the current item is the last list item in
132  *  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     assert_locked(p_mlp);
138
139     /* We are entered with libvlc_media_list_lock(p_mlp->p_list) */
140     libvlc_media_list_path_t ret;
141     libvlc_media_list_t * p_parent_of_playing_item;
142     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             return NULL;
148         return libvlc_media_list_path_with_root_index(0);
149     }
150
151     p_sublist_of_playing_item = libvlc_media_list_sublist_at_path(
152                             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         libvlc_media_list_release(p_sublist_of_playing_item);
159         return libvlc_media_list_path_copy_by_appending(p_mlp->current_playing_item_path, 0);
160     }
161
162     /* Try to catch parent element */
163     p_parent_of_playing_item = libvlc_media_list_parentlist_at_path(p_mlp->p_mlist,
164                             p_mlp->current_playing_item_path);
165
166     int depth = libvlc_media_list_path_depth(p_mlp->current_playing_item_path);
167     if (depth < 1 || !p_parent_of_playing_item)
168         return NULL;
169
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     {
176         depth--;
177         if (depth <= 0)
178         {
179             if(b_loop)
180             {
181                 ret[0] = 0;
182                 ret[1] = -1;
183                 break;
184             }
185             else
186             {
187                 free(ret);
188                 libvlc_media_list_release(p_parent_of_playing_item);
189                 return NULL;
190             }
191         }
192         ret[depth] = -1;
193         ret[depth-1]++;
194         p_parent_of_playing_item  = libvlc_media_list_parentlist_at_path(
195                                         p_mlp->p_mlist,
196                                         ret);
197     }
198
199     libvlc_media_list_release(p_parent_of_playing_item);
200     return ret;
201 }
202
203 /**************************************************************************
204  *       find_last_item (private)
205  *
206  *  Returns the path of the last descendant of a given item path.
207  *  Note: Due to the recursive nature of the function and the need to free
208  *        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           passed to the function (i.e. item = find_last_item(list, item); )
211  **************************************************************************/
212 static libvlc_media_list_path_t
213 find_last_item( libvlc_media_list_t * p_mlist, libvlc_media_list_path_t current_item )
214 {
215     libvlc_media_list_t * p_sublist = libvlc_media_list_sublist_at_path(p_mlist, current_item);
216     libvlc_media_list_path_t last_item_path = current_item;
217
218     if(p_sublist)
219     {
220         int i_count = libvlc_media_list_count(p_sublist);
221         if(i_count > 0)
222         {
223             /* Add the last sublist item to the path. */
224             last_item_path = libvlc_media_list_path_copy_by_appending(current_item, i_count - 1);
225             free(current_item);
226             /* Check that sublist item for more descendants. */
227             last_item_path = find_last_item(p_mlist, last_item_path);
228         }
229
230         libvlc_media_list_release(p_sublist);
231     }
232
233     return last_item_path;
234 }
235
236 /**************************************************************************
237  *       get_previous_path (private)
238  *
239  *  Returns the path to the preceding item in the list.
240  *  If looping is specified and the current item is the first list item in
241  *  the list it will return the last descendant of the last item in the list.
242  **************************************************************************/
243 static libvlc_media_list_path_t
244 get_previous_path(libvlc_media_list_player_t * p_mlp, bool b_loop)
245 {
246     assert_locked(p_mlp);
247
248     /* We are entered with libvlc_media_list_lock(p_mlp->p_list) */
249     libvlc_media_list_path_t ret;
250     libvlc_media_list_t * p_parent_of_playing_item;
251
252     if (!p_mlp->current_playing_item_path)
253     {
254         if (!libvlc_media_list_count(p_mlp->p_mlist))
255             return NULL;
256         return libvlc_media_list_path_with_root_index(0);
257     }
258
259     /* Try to catch parent element */
260     p_parent_of_playing_item = libvlc_media_list_parentlist_at_path(
261                                             p_mlp->p_mlist,
262                                             p_mlp->current_playing_item_path);
263
264     int depth = libvlc_media_list_path_depth(p_mlp->current_playing_item_path);
265     if (depth < 1 || !p_parent_of_playing_item)
266         return NULL;
267
268     /* Set the return path to the current path */
269     ret = libvlc_media_list_path_copy(p_mlp->current_playing_item_path);
270
271     /* Change the return path to the previous list entry */
272     ret[depth - 1]--; /* set to previous element */
273     ret[depth] = -1;
274
275     /* Is the return path is beyond the start of the current list? */
276     if(ret[depth - 1] < 0)
277     {
278         /* Move to parent of current item */
279         depth--;
280
281         /* Are we at the root level of the tree? */
282         if (depth <= 0)
283         {
284             // Is looping enabled?
285             if(b_loop)
286             {
287                 int i_count = libvlc_media_list_count(p_parent_of_playing_item);
288
289                 /* Set current play item to the last element in the list */
290                 ret[0] = i_count - 1;
291                 ret[1] = -1;
292
293                 /* Set the return path to the last descendant item of the current item */
294                 ret = find_last_item(p_mlp->p_mlist, ret);
295             }
296             else
297             {
298                 /* No looping so return empty path. */
299                 free(ret);
300                 ret = NULL;
301             }
302         }
303         else
304         {
305             /* This is the case of moving backward from the beginning of the
306             *  subitem list to its parent item.
307             *  This ensures that current path is properly terminated to
308             *  use that parent.
309             */
310             ret[depth] = -1;
311         }
312     }
313     else
314     {
315         ret = find_last_item(p_mlp->p_mlist, ret);
316     }
317
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     vlc_mutex_lock(&p_mlp->mp_callback_lock);
332     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  *       playlist_item_deleted (private) (Event Callback)
339  **************************************************************************/
340 static void
341 mlist_item_deleted(const libvlc_event_t * p_event, void * p_user_data)
342 {
343     // Nothing to do. For now.
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     assert_locked(p_mlp);
365     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     assert_locked(p_mlp);
387     if (!p_mlp->p_mi) return;
388
389     // From now on, media_player callback won't be relevant.
390     p_mlp->are_mp_callback_cancelled = true;
391
392     // Allow callbacks to run, because detach() will wait until all callbacks are processed.
393     // This is safe because only callbacks are allowed, and there execution will be cancelled.
394     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     // Now, lock back the callback lock. No more callback will be present from this point.
398     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  * Playlist lock should be held
409  **************************************************************************/
410 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     /* First, save the new path that we are going to play */
416     if (p_mlp->current_playing_item_path != path)
417     {
418         free(p_mlp->current_playing_item_path);
419         p_mlp->current_playing_item_path = path;
420     }
421
422     if (!path)
423         return;
424
425     libvlc_media_t * p_md;
426     p_md = libvlc_media_list_item_at_path(p_mlp->p_mlist, path);
427     if (!p_md)
428         return;
429
430     /* Make sure media_player_reached_end() won't get called */
431     uninstall_media_player_observer(p_mlp);
432
433     /* Create a new media_player if there is none */
434     if (!p_mlp->p_mi)
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     install_media_player_observer(p_mlp);
440     libvlc_media_release(p_md); /* for libvlc_media_list_item_at_index */
441 }
442
443 /*
444  * Public libvlc functions
445  */
446
447 /**************************************************************************
448  *         new (Public)
449  **************************************************************************/
450 libvlc_media_list_player_t *
451 libvlc_media_list_player_new(libvlc_instance_t * p_instance)
452 {
453     libvlc_media_list_player_t * p_mlp;
454     p_mlp = calloc( 1, sizeof(libvlc_media_list_player_t) );
455     if (unlikely(p_mlp == NULL))
456     {
457         libvlc_printerr("Not enough memory");
458         return NULL;
459     }
460
461     p_mlp->p_event_manager = libvlc_event_manager_new(p_mlp, p_instance);
462     if (unlikely(p_mlp->p_event_manager == NULL))
463     {
464         free (p_mlp);
465         return NULL;
466     }
467
468     libvlc_retain(p_instance);
469     p_mlp->p_libvlc_instance = p_instance;
470     p_mlp->i_refcount = 1;
471     vlc_mutex_init(&p_mlp->object_lock);
472     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         return;
486
487     lock(p_mlp);
488     p_mlp->i_refcount--;
489     if (p_mlp->i_refcount > 0)
490     {
491         unlock(p_mlp);
492         return;
493     }
494
495     assert(p_mlp->i_refcount == 0);
496
497     /* Keep the lock(), because the uninstall functions
498      * check for it. That's convenient. */
499
500     if (p_mlp->p_mi)
501     {
502         uninstall_media_player_observer(p_mlp);
503         libvlc_media_player_release(p_mlp->p_mi);
504     }
505     if (p_mlp->p_mlist)
506     {
507         uninstall_playlist_observer(p_mlp);
508         libvlc_media_list_release(p_mlp->p_mlist);
509     }
510
511     unlock(p_mlp);
512     vlc_mutex_destroy(&p_mlp->object_lock);
513     vlc_mutex_destroy(&p_mlp->mp_callback_lock);
514
515     libvlc_event_manager_release(p_mlp->p_event_manager);
516
517     free(p_mlp->current_playing_item_path);
518     libvlc_release(p_mlp->p_libvlc_instance);
519     free(p_mlp);
520 }
521
522 /**************************************************************************
523  *        retain (Public)
524  **************************************************************************/
525 void libvlc_media_list_player_retain(libvlc_media_list_player_t * p_mlp)
526 {
527     if (!p_mlp)
528         return;
529
530     lock(p_mlp);
531     p_mlp->i_refcount++;
532     unlock(p_mlp);
533 }
534
535 /**************************************************************************
536  *        event_manager (Public)
537  **************************************************************************/
538 libvlc_event_manager_t *
539 libvlc_media_list_player_event_manager(libvlc_media_list_player_t * p_mlp)
540 {
541     return p_mlp->p_event_manager;
542 }
543
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         uninstall_media_player_observer(p_mlp);
554         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         uninstall_playlist_observer(p_mlp);
575         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  *        Play (Public)
587  **************************************************************************/
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  *        Pause (Public)
604  **************************************************************************/
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  *        is_playing (Public)
619  **************************************************************************/
620 int
621 libvlc_media_list_player_is_playing(libvlc_media_list_player_t * p_mlp)
622 {
623     if (!p_mlp->p_mi)
624     {
625         return libvlc_NothingSpecial;
626     }
627     libvlc_state_t state = libvlc_media_player_get_state(p_mlp->p_mi);
628     return (state == libvlc_Opening) || (state == libvlc_Buffering) ||
629            (state == libvlc_Playing);
630 }
631
632 /**************************************************************************
633  *        State (Public)
634  **************************************************************************/
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  *        Play item at index (Public)
645  **************************************************************************/
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     /* Send the next item event */
654     libvlc_event_t event;
655     event.type = libvlc_MediaListPlayerNextItemSet;
656     libvlc_event_send(p_mlp->p_event_manager, &event);
657     return 0;
658 }
659
660 /**************************************************************************
661  *        Play item (Public)
662  **************************************************************************/
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     libvlc_media_list_path_t path = libvlc_media_list_path_of_item(p_mlp->p_mlist, p_md);
667     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  *       Stop (Private)
682  *
683  * 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  *       Stop (Public)
703  **************************************************************************/
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     libvlc_media_list_path_t path = p_mlp->current_playing_item_path;
733
734     if(p_mlp->e_playback_mode != libvlc_playback_mode_repeat)
735     {
736         bool b_loop = (p_mlp->e_playback_mode == libvlc_playback_mode_loop);
737
738         if(i_relative_position > 0)
739         {
740             do
741             {
742                 path = get_next_path(p_mlp, b_loop);
743                 set_current_playing_item(p_mlp, path);
744                 --i_relative_position;
745             }
746             while(i_relative_position > 0);
747         }
748         else if(i_relative_position < 0)
749         {
750             do
751             {
752                 path = get_previous_path(p_mlp, b_loop);
753                 set_current_playing_item(p_mlp, path);
754                 ++i_relative_position;
755             }
756             while (i_relative_position < 0);
757         }
758     }
759     else
760     {
761         set_current_playing_item(p_mlp, path);
762     }
763
764 #ifdef DEBUG_MEDIA_LIST_PLAYER
765     printf("Playing:");
766     libvlc_media_list_path_dump(path);
767 #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     /* Send the next item event */
780     libvlc_event_t event;
781     event.type = libvlc_MediaListPlayerNextItemSet;
782     libvlc_media_t * p_md = libvlc_media_list_item_at_path(p_mlp->p_mlist, path);
783     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);
786     return 0;
787 }
788
789 /**************************************************************************
790  *       Next (Public)
791  **************************************************************************/
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  *       Previous (Public)
802  **************************************************************************/
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  *       Set Playback Mode (Public)
813  **************************************************************************/
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     lock(p_mlp);
819     p_mlp->e_playback_mode = e_mode;
820     unlock(p_mlp);
821 }