]> git.sesse.net Git - vlc/blob - lib/media_list_player.c
bluray: ARGB overlay support (BD-J)
[vlc] / 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,
474             libvlc_MediaListPlayerNextItemSet );
475     libvlc_event_manager_register_event_type( p_mlp->p_event_manager,
476             libvlc_MediaListPlayerStopped );
477     p_mlp->e_playback_mode = libvlc_playback_mode_default;
478
479     return p_mlp;
480 }
481
482 /**************************************************************************
483  *         release (Public)
484  **************************************************************************/
485 void libvlc_media_list_player_release(libvlc_media_list_player_t * p_mlp)
486 {
487     if (!p_mlp)
488         return;
489
490     lock(p_mlp);
491     p_mlp->i_refcount--;
492     if (p_mlp->i_refcount > 0)
493     {
494         unlock(p_mlp);
495         return;
496     }
497
498     assert(p_mlp->i_refcount == 0);
499
500     /* Keep the lock(), because the uninstall functions
501      * check for it. That's convenient. */
502
503     if (p_mlp->p_mi)
504     {
505         uninstall_media_player_observer(p_mlp);
506         libvlc_media_player_release(p_mlp->p_mi);
507     }
508     if (p_mlp->p_mlist)
509     {
510         uninstall_playlist_observer(p_mlp);
511         libvlc_media_list_release(p_mlp->p_mlist);
512     }
513
514     unlock(p_mlp);
515     vlc_mutex_destroy(&p_mlp->object_lock);
516     vlc_mutex_destroy(&p_mlp->mp_callback_lock);
517
518     libvlc_event_manager_release(p_mlp->p_event_manager);
519
520     free(p_mlp->current_playing_item_path);
521     libvlc_release(p_mlp->p_libvlc_instance);
522     free(p_mlp);
523 }
524
525 /**************************************************************************
526  *        retain (Public)
527  **************************************************************************/
528 void libvlc_media_list_player_retain(libvlc_media_list_player_t * p_mlp)
529 {
530     if (!p_mlp)
531         return;
532
533     lock(p_mlp);
534     p_mlp->i_refcount++;
535     unlock(p_mlp);
536 }
537
538 /**************************************************************************
539  *        event_manager (Public)
540  **************************************************************************/
541 libvlc_event_manager_t *
542 libvlc_media_list_player_event_manager(libvlc_media_list_player_t * p_mlp)
543 {
544     return p_mlp->p_event_manager;
545 }
546
547 /**************************************************************************
548  *        set_media_player (Public)
549  **************************************************************************/
550 void libvlc_media_list_player_set_media_player(libvlc_media_list_player_t * p_mlp, libvlc_media_player_t * p_mi)
551 {
552     lock(p_mlp);
553
554     if (p_mlp->p_mi)
555     {
556         uninstall_media_player_observer(p_mlp);
557         libvlc_media_player_release(p_mlp->p_mi);
558     }
559     libvlc_media_player_retain(p_mi);
560     p_mlp->p_mi = p_mi;
561
562     install_media_player_observer(p_mlp);
563
564     unlock(p_mlp);
565 }
566
567 /**************************************************************************
568  *       set_media_list (Public)
569  **************************************************************************/
570 void libvlc_media_list_player_set_media_list(libvlc_media_list_player_t * p_mlp, libvlc_media_list_t * p_mlist)
571 {
572     assert (p_mlist);
573
574     lock(p_mlp);
575     if (p_mlp->p_mlist)
576     {
577         uninstall_playlist_observer(p_mlp);
578         libvlc_media_list_release(p_mlp->p_mlist);
579     }
580     libvlc_media_list_retain(p_mlist);
581     p_mlp->p_mlist = p_mlist;
582
583     install_playlist_observer(p_mlp);
584
585     unlock(p_mlp);
586 }
587
588 /**************************************************************************
589  *        Play (Public)
590  **************************************************************************/
591 void libvlc_media_list_player_play(libvlc_media_list_player_t * p_mlp)
592 {
593     lock(p_mlp);
594     if (!p_mlp->current_playing_item_path)
595     {
596         set_relative_playlist_position_and_play(p_mlp, 1);
597         unlock(p_mlp);
598         return; /* Will set to play */
599     }
600     libvlc_media_player_play(p_mlp->p_mi);
601     unlock(p_mlp);
602 }
603
604
605 /**************************************************************************
606  *        Pause (Public)
607  **************************************************************************/
608 void libvlc_media_list_player_pause(libvlc_media_list_player_t * p_mlp)
609 {
610     lock(p_mlp);
611     if (!p_mlp->p_mi)
612     {
613         unlock(p_mlp);
614         return;
615     }
616     libvlc_media_player_pause(p_mlp->p_mi);
617     unlock(p_mlp);
618 }
619
620 /**************************************************************************
621  *        is_playing (Public)
622  **************************************************************************/
623 int
624 libvlc_media_list_player_is_playing(libvlc_media_list_player_t * p_mlp)
625 {
626     if (!p_mlp->p_mi)
627     {
628         return libvlc_NothingSpecial;
629     }
630     libvlc_state_t state = libvlc_media_player_get_state(p_mlp->p_mi);
631     return (state == libvlc_Opening) || (state == libvlc_Buffering) ||
632            (state == libvlc_Playing);
633 }
634
635 /**************************************************************************
636  *        State (Public)
637  **************************************************************************/
638 libvlc_state_t
639 libvlc_media_list_player_get_state(libvlc_media_list_player_t * p_mlp)
640 {
641     if (!p_mlp->p_mi)
642         return libvlc_Ended;
643     return libvlc_media_player_get_state(p_mlp->p_mi);
644 }
645
646 /**************************************************************************
647  *        Play item at index (Public)
648  **************************************************************************/
649 int libvlc_media_list_player_play_item_at_index(libvlc_media_list_player_t * p_mlp, int i_index)
650 {
651     lock(p_mlp);
652     libvlc_media_list_path_t path = libvlc_media_list_path_with_root_index(i_index);
653     set_current_playing_item(p_mlp, path);
654     libvlc_media_player_play(p_mlp->p_mi);
655     unlock(p_mlp);
656
657     /* Send the next item event */
658     libvlc_event_t event;
659     event.type = libvlc_MediaListPlayerNextItemSet;
660     libvlc_media_t * p_md = libvlc_media_list_item_at_path(p_mlp->p_mlist, path);
661     event.u.media_list_player_next_item_set.item = p_md;
662     libvlc_event_send(p_mlp->p_event_manager, &event);
663     libvlc_media_release(p_md);
664     return 0;
665 }
666
667 /**************************************************************************
668  *        Play item (Public)
669  **************************************************************************/
670 int libvlc_media_list_player_play_item(libvlc_media_list_player_t * p_mlp, libvlc_media_t * p_md)
671 {
672     lock(p_mlp);
673     libvlc_media_list_path_t path = libvlc_media_list_path_of_item(p_mlp->p_mlist, p_md);
674     if (!path)
675     {
676         libvlc_printerr("Item not found in media list");
677         unlock(p_mlp);
678         return -1;
679     }
680
681     set_current_playing_item(p_mlp, path);
682     libvlc_media_player_play(p_mlp->p_mi);
683     unlock(p_mlp);
684     return 0;
685 }
686
687 /**************************************************************************
688  *       Stop (Private)
689  *
690  * Lock must be held.
691  **************************************************************************/
692 static void stop(libvlc_media_list_player_t * p_mlp)
693 {
694     assert_locked(p_mlp);
695
696     if (p_mlp->p_mi)
697     {
698         /* We are not interested in getting media stop event now */
699         uninstall_media_player_observer(p_mlp);
700         libvlc_media_player_stop(p_mlp->p_mi);
701         install_media_player_observer(p_mlp);
702     }
703
704     free(p_mlp->current_playing_item_path);
705     p_mlp->current_playing_item_path = NULL;
706
707     /* Send the event */
708     libvlc_event_t event;
709     event.type = libvlc_MediaListPlayerStopped;
710     libvlc_event_send(p_mlp->p_event_manager, &event);
711 }
712
713 /**************************************************************************
714  *       Stop (Public)
715  **************************************************************************/
716 void libvlc_media_list_player_stop(libvlc_media_list_player_t * p_mlp)
717 {
718     lock(p_mlp);
719     stop(p_mlp);
720     unlock(p_mlp);
721 }
722
723 /**************************************************************************
724  *       Set relative playlist position and play (Private)
725  *
726  * Sets the currently played item to the given relative play item position
727  * (based on the currently playing item) and then begins the new item playback.
728  * Lock must be held.
729  **************************************************************************/
730 static int set_relative_playlist_position_and_play(
731                                       libvlc_media_list_player_t * p_mlp,
732                                       int i_relative_position)
733 {
734     assert_locked(p_mlp);
735
736     if (!p_mlp->p_mlist)
737     {
738         libvlc_printerr("No media list");
739         return -1;
740     }
741
742     libvlc_media_list_lock(p_mlp->p_mlist);
743
744     libvlc_media_list_path_t path = p_mlp->current_playing_item_path;
745
746     if(p_mlp->e_playback_mode != libvlc_playback_mode_repeat)
747     {
748         bool b_loop = (p_mlp->e_playback_mode == libvlc_playback_mode_loop);
749
750         if(i_relative_position > 0)
751         {
752             do
753             {
754                 path = get_next_path(p_mlp, b_loop);
755                 set_current_playing_item(p_mlp, path);
756                 --i_relative_position;
757             }
758             while(i_relative_position > 0);
759         }
760         else if(i_relative_position < 0)
761         {
762             do
763             {
764                 path = get_previous_path(p_mlp, b_loop);
765                 set_current_playing_item(p_mlp, path);
766                 ++i_relative_position;
767             }
768             while (i_relative_position < 0);
769         }
770     }
771     else
772     {
773         set_current_playing_item(p_mlp, path);
774     }
775
776 #ifdef DEBUG_MEDIA_LIST_PLAYER
777     printf("Playing:");
778     libvlc_media_list_path_dump(path);
779 #endif
780
781     if (!path)
782     {
783         libvlc_media_list_unlock(p_mlp->p_mlist);
784         return -1;
785     }
786
787     libvlc_media_player_play(p_mlp->p_mi);
788
789     libvlc_media_list_unlock(p_mlp->p_mlist);
790
791     /* Send the next item event */
792     libvlc_event_t event;
793     event.type = libvlc_MediaListPlayerNextItemSet;
794     libvlc_media_t * p_md = libvlc_media_list_item_at_path(p_mlp->p_mlist, path);
795     event.u.media_list_player_next_item_set.item = p_md;
796     libvlc_event_send(p_mlp->p_event_manager, &event);
797     libvlc_media_release(p_md);
798     return 0;
799 }
800
801 /**************************************************************************
802  *       Next (Public)
803  **************************************************************************/
804 int libvlc_media_list_player_next(libvlc_media_list_player_t * p_mlp)
805 {
806     lock(p_mlp);
807     int failure = set_relative_playlist_position_and_play(p_mlp, 1);
808     unlock(p_mlp);
809     return failure;
810 }
811
812 /**************************************************************************
813  *       Previous (Public)
814  **************************************************************************/
815 int libvlc_media_list_player_previous(libvlc_media_list_player_t * p_mlp)
816 {
817     lock(p_mlp);
818     int failure = set_relative_playlist_position_and_play(p_mlp, -1);
819     unlock(p_mlp);
820     return failure;
821 }
822
823 /**************************************************************************
824  *       Set Playback Mode (Public)
825  **************************************************************************/
826 void libvlc_media_list_player_set_playback_mode(
827                                             libvlc_media_list_player_t * p_mlp,
828                                             libvlc_playback_mode_t e_mode )
829 {
830     lock(p_mlp);
831     p_mlp->e_playback_mode = e_mode;
832     unlock(p_mlp);
833 }