]> git.sesse.net Git - vlc/blob - src/control/media_list_player.c
Use var_Inherit* instead of var_CreateGet*.
[vlc] / src / control / media_list_player.c
1 /*****************************************************************************
2  * media_list_player.c: libvlc new API media_list player functions
3  *****************************************************************************
4  * Copyright (C) 2007 the VideoLAN team
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
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 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 General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, 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  *        event_manager (Public)
524  **************************************************************************/
525 libvlc_event_manager_t *
526 libvlc_media_list_player_event_manager(libvlc_media_list_player_t * p_mlp)
527 {
528     return p_mlp->p_event_manager;
529 }
530
531 /**************************************************************************
532  *        set_media_player (Public)
533  **************************************************************************/
534 void libvlc_media_list_player_set_media_player(libvlc_media_list_player_t * p_mlp, libvlc_media_player_t * p_mi)
535 {
536     lock(p_mlp);
537
538     if (p_mlp->p_mi)
539     {
540         uninstall_media_player_observer(p_mlp);
541         libvlc_media_player_release(p_mlp->p_mi);
542     }
543     libvlc_media_player_retain(p_mi);
544     p_mlp->p_mi = p_mi;
545
546     install_media_player_observer(p_mlp);
547
548     unlock(p_mlp);
549 }
550
551 /**************************************************************************
552  *       set_media_list (Public)
553  **************************************************************************/
554 void libvlc_media_list_player_set_media_list(libvlc_media_list_player_t * p_mlp, libvlc_media_list_t * p_mlist)
555 {
556     assert (p_mlist);
557
558     lock(p_mlp);
559     if (p_mlp->p_mlist)
560     {
561         uninstall_playlist_observer(p_mlp);
562         libvlc_media_list_release(p_mlp->p_mlist);
563     }
564     libvlc_media_list_retain(p_mlist);
565     p_mlp->p_mlist = p_mlist;
566
567     install_playlist_observer(p_mlp);
568
569     unlock(p_mlp);
570 }
571
572 /**************************************************************************
573  *        Play (Public)
574  **************************************************************************/
575 void libvlc_media_list_player_play(libvlc_media_list_player_t * p_mlp)
576 {
577     lock(p_mlp);
578     if (!p_mlp->current_playing_item_path)
579     {
580         set_relative_playlist_position_and_play(p_mlp, 1);
581         unlock(p_mlp);
582         return; /* Will set to play */
583     }
584     libvlc_media_player_play(p_mlp->p_mi);
585     unlock(p_mlp);
586 }
587
588
589 /**************************************************************************
590  *        Pause (Public)
591  **************************************************************************/
592 void libvlc_media_list_player_pause(libvlc_media_list_player_t * p_mlp)
593 {
594     lock(p_mlp);
595     if (!p_mlp->p_mi)
596     {
597         unlock(p_mlp);
598         return;
599     }
600     libvlc_media_player_pause(p_mlp->p_mi);
601     unlock(p_mlp);
602 }
603
604 /**************************************************************************
605  *        is_playing (Public)
606  **************************************************************************/
607 int
608 libvlc_media_list_player_is_playing(libvlc_media_list_player_t * p_mlp)
609 {
610     if (!p_mlp->p_mi)
611     {
612         return libvlc_NothingSpecial;
613     }
614     libvlc_state_t state = libvlc_media_player_get_state(p_mlp->p_mi);
615     return (state == libvlc_Opening) || (state == libvlc_Buffering) ||
616            (state == libvlc_Playing);
617 }
618
619 /**************************************************************************
620  *        State (Public)
621  **************************************************************************/
622 libvlc_state_t
623 libvlc_media_list_player_get_state(libvlc_media_list_player_t * p_mlp)
624 {
625     if (!p_mlp->p_mi)
626         return libvlc_Ended;
627     return libvlc_media_player_get_state(p_mlp->p_mi);
628 }
629
630 /**************************************************************************
631  *        Play item at index (Public)
632  **************************************************************************/
633 int libvlc_media_list_player_play_item_at_index(libvlc_media_list_player_t * p_mlp, int i_index)
634 {
635     lock(p_mlp);
636     set_current_playing_item(p_mlp, libvlc_media_list_path_with_root_index(i_index));
637     libvlc_media_player_play(p_mlp->p_mi);
638     unlock(p_mlp);
639
640     /* Send the next item event */
641     libvlc_event_t event;
642     event.type = libvlc_MediaListPlayerNextItemSet;
643     libvlc_event_send(p_mlp->p_event_manager, &event);
644     return 0;
645 }
646
647 /**************************************************************************
648  *        Play item (Public)
649  **************************************************************************/
650 int libvlc_media_list_player_play_item(libvlc_media_list_player_t * p_mlp, libvlc_media_t * p_md)
651 {
652     lock(p_mlp);
653     libvlc_media_list_path_t path = libvlc_media_list_path_of_item(p_mlp->p_mlist, p_md);
654     if (!path)
655     {
656         libvlc_printerr("Item not found in media list");
657         unlock(p_mlp);
658         return -1;
659     }
660
661     set_current_playing_item(p_mlp, path);
662     libvlc_media_player_play(p_mlp->p_mi);
663     unlock(p_mlp);
664     return 0;
665 }
666
667 /**************************************************************************
668  *       Stop (Private)
669  *
670  * Lock must be held.
671  **************************************************************************/
672 static void stop(libvlc_media_list_player_t * p_mlp)
673 {
674     assert_locked(p_mlp);
675
676     if (p_mlp->p_mi)
677     {
678         /* We are not interested in getting media stop event now */
679         uninstall_media_player_observer(p_mlp);
680         libvlc_media_player_stop(p_mlp->p_mi);
681         install_media_player_observer(p_mlp);
682     }
683
684     free(p_mlp->current_playing_item_path);
685     p_mlp->current_playing_item_path = NULL;
686 }
687
688 /**************************************************************************
689  *       Stop (Public)
690  **************************************************************************/
691 void libvlc_media_list_player_stop(libvlc_media_list_player_t * p_mlp)
692 {
693     lock(p_mlp);
694     stop(p_mlp);
695     unlock(p_mlp);
696 }
697
698 /**************************************************************************
699  *       Set relative playlist position and play (Private)
700  *
701  * Sets the currently played item to the given relative play item position
702  * (based on the currently playing item) and then begins the new item playback.
703  * Lock must be held.
704  **************************************************************************/
705 static int set_relative_playlist_position_and_play(
706                                       libvlc_media_list_player_t * p_mlp,
707                                       int i_relative_position)
708 {
709     assert_locked(p_mlp);
710
711     if (!p_mlp->p_mlist)
712     {
713         libvlc_printerr("No media list");
714         return -1;
715     }
716
717     libvlc_media_list_lock(p_mlp->p_mlist);
718
719     libvlc_media_list_path_t path = p_mlp->current_playing_item_path;
720
721     if(p_mlp->e_playback_mode != libvlc_playback_mode_repeat)
722     {
723         bool b_loop = (p_mlp->e_playback_mode == libvlc_playback_mode_loop);
724
725         if(i_relative_position > 0)
726         {
727             do
728             {
729                 path = get_next_path(p_mlp, b_loop);
730                 set_current_playing_item(p_mlp, path);
731                 --i_relative_position;
732             }
733             while(i_relative_position > 0);
734         }
735         else if(i_relative_position < 0)
736         {
737             do
738             {
739                 path = get_previous_path(p_mlp, b_loop);
740                 set_current_playing_item(p_mlp, path);
741                 ++i_relative_position;
742             }
743             while (i_relative_position < 0);
744         }
745     }
746     else
747     {
748         set_current_playing_item(p_mlp, path);
749     }
750
751 #ifdef DEBUG_MEDIA_LIST_PLAYER
752     printf("Playing:");
753     libvlc_media_list_path_dump(path);
754 #endif
755
756     if (!path)
757     {
758         libvlc_media_list_unlock(p_mlp->p_mlist);
759         return -1;
760     }
761
762     libvlc_media_player_play(p_mlp->p_mi);
763
764     libvlc_media_list_unlock(p_mlp->p_mlist);
765
766     /* Send the next item event */
767     libvlc_event_t event;
768     event.type = libvlc_MediaListPlayerNextItemSet;
769     libvlc_media_t * p_md = libvlc_media_list_item_at_path(p_mlp->p_mlist, path);
770     event.u.media_list_player_next_item_set.item = p_md;
771     libvlc_event_send(p_mlp->p_event_manager, &event);
772     libvlc_media_release(p_md);
773     return 0;
774 }
775
776 /**************************************************************************
777  *       Next (Public)
778  **************************************************************************/
779 int libvlc_media_list_player_next(libvlc_media_list_player_t * p_mlp)
780 {
781     lock(p_mlp);
782     int failure = set_relative_playlist_position_and_play(p_mlp, 1);
783     unlock(p_mlp);
784     return failure;
785 }
786
787 /**************************************************************************
788  *       Previous (Public)
789  **************************************************************************/
790 int libvlc_media_list_player_previous(libvlc_media_list_player_t * p_mlp)
791 {
792     lock(p_mlp);
793     int failure = set_relative_playlist_position_and_play(p_mlp, -1);
794     unlock(p_mlp);
795     return failure;
796 }
797
798 /**************************************************************************
799  *       Set Playback Mode (Public)
800  **************************************************************************/
801 void libvlc_media_list_player_set_playback_mode(
802                                             libvlc_media_list_player_t * p_mlp,
803                                             libvlc_playback_mode_t e_mode )
804 {
805     lock(p_mlp);
806     p_mlp->e_playback_mode = e_mode;
807     unlock(p_mlp);
808 }