libvlc: add renderer_discoverer external API
[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-2015 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Pierre d'Herbemont <pdherbemont # videolan.org>
8  *          Niles Bindel <zaggal69 # gmail.com>
9  *          RĂ©mi Denis-Courmont
10  *
11  * This program is free software; you can redistribute it and/or modify it
12  * under the terms of the GNU Lesser General Public License as published by
13  * the Free Software Foundation; either version 2.1 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * along with this program; if not, write to the Free Software Foundation,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include <vlc/libvlc.h>
31 #include <vlc/libvlc_media.h>
32 #include <vlc/libvlc_media_list.h>
33 #include <vlc/libvlc_media_player.h>
34 #include <vlc/libvlc_media_list_player.h>
35 #include <vlc/libvlc_events.h>
36 #include <assert.h>
37
38 #include "libvlc_internal.h"
39
40 #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 /* This is a very dummy implementation of playlist on top of
46  * media_list and media_player.
47  *
48  * All this code is doing is simply computing the next item
49  * of a tree of media_list (see get_next_index()), and play
50  * the next item when the current is over. This is happening
51  * via the event callback media_player_reached_end().
52  *
53  * This is thread safe, and we use a two keys (locks) scheme
54  * to discriminate between callbacks and regular uses.
55  */
56
57 struct libvlc_media_list_player_t
58 {
59     libvlc_event_manager_t *    p_event_manager;
60     int                         i_refcount;
61     int                         seek_offset;
62     /* Protect access to this structure. */
63     vlc_mutex_t                 object_lock;
64     /* Protect access to this structure and from callback execution. */
65     vlc_mutex_t                 mp_callback_lock;
66     vlc_cond_t                  seek_pending;
67     libvlc_media_list_path_t    current_playing_item_path;
68     libvlc_media_t *            p_current_playing_item;
69     libvlc_media_list_t *       p_mlist;
70     libvlc_media_player_t *     p_mi;
71     libvlc_playback_mode_t      e_playback_mode;
72
73     vlc_thread_t                thread;
74 };
75
76 /* This is not yet exported by libvlccore */
77 static inline void vlc_assert_locked(vlc_mutex_t *mutex)
78 {
79     VLC_UNUSED(mutex);
80 }
81
82 /*
83  * Forward declaration
84  */
85
86 static
87 int set_relative_playlist_position_and_play(libvlc_media_list_player_t *p_mlp,
88                                             int i_relative_position);
89 static void stop(libvlc_media_list_player_t * p_mlp);
90
91 /*
92  * Private functions
93  */
94
95 /**************************************************************************
96  * Shortcuts
97  **************************************************************************/
98 static inline void lock(libvlc_media_list_player_t * p_mlp)
99 {
100     // Obtain an access to this structure
101     vlc_mutex_lock(&p_mlp->object_lock);
102
103     // Make sure no callback will occurs at the same time
104     vlc_mutex_lock(&p_mlp->mp_callback_lock);
105 }
106
107 static inline void unlock(libvlc_media_list_player_t * p_mlp)
108 {
109     vlc_mutex_unlock(&p_mlp->mp_callback_lock);
110     vlc_mutex_unlock(&p_mlp->object_lock);
111 }
112
113 static inline void assert_locked(libvlc_media_list_player_t * p_mlp)
114 {
115     vlc_assert_locked(&p_mlp->mp_callback_lock);
116 }
117
118 static inline libvlc_event_manager_t * mlist_em(libvlc_media_list_player_t * p_mlp)
119 {
120     assert_locked(p_mlp);
121     return libvlc_media_list_event_manager(p_mlp->p_mlist);
122 }
123
124 static inline libvlc_event_manager_t * mplayer_em(libvlc_media_list_player_t * p_mlp)
125 {
126     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  *  If looping is specified and the current item is the last list item in
135  *  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     assert_locked(p_mlp);
141
142     /* We are entered with libvlc_media_list_lock(p_mlp->p_list) */
143     libvlc_media_list_path_t ret;
144     libvlc_media_list_t * p_parent_of_playing_item;
145     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))
150             return NULL;
151         return libvlc_media_list_path_with_root_index(0);
152     }
153
154     p_sublist_of_playing_item = libvlc_media_list_sublist_at_path(
155                             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         libvlc_media_list_release(p_sublist_of_playing_item);
162         return libvlc_media_list_path_copy_by_appending(p_mlp->current_playing_item_path, 0);
163     }
164
165     /* Try to catch parent element */
166     p_parent_of_playing_item = libvlc_media_list_parentlist_at_path(p_mlp->p_mlist,
167                             p_mlp->current_playing_item_path);
168
169     int depth = libvlc_media_list_path_depth(p_mlp->current_playing_item_path);
170     if (depth < 1 || !p_parent_of_playing_item)
171         return NULL;
172
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     {
179         depth--;
180         if (depth <= 0)
181         {
182             if(b_loop)
183             {
184                 ret[0] = 0;
185                 ret[1] = -1;
186                 break;
187             }
188             else
189             {
190                 free(ret);
191                 libvlc_media_list_release(p_parent_of_playing_item);
192                 return NULL;
193             }
194         }
195         ret[depth] = -1;
196         ret[depth-1]++;
197         p_parent_of_playing_item  = libvlc_media_list_parentlist_at_path(
198                                         p_mlp->p_mlist,
199                                         ret);
200     }
201
202     libvlc_media_list_release(p_parent_of_playing_item);
203     return ret;
204 }
205
206 /**************************************************************************
207  *       find_last_item (private)
208  *
209  *  Returns the path of the last descendant of a given item path.
210  *  Note: Due to the recursive nature of the function and the need to free
211  *        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           passed to the function (i.e. item = find_last_item(list, item); )
214  **************************************************************************/
215 static libvlc_media_list_path_t
216 find_last_item( libvlc_media_list_t * p_mlist, libvlc_media_list_path_t current_item )
217 {
218     libvlc_media_list_t * p_sublist = libvlc_media_list_sublist_at_path(p_mlist, current_item);
219     libvlc_media_list_path_t last_item_path = current_item;
220
221     if(p_sublist)
222     {
223         int i_count = libvlc_media_list_count(p_sublist);
224         if(i_count > 0)
225         {
226             /* Add the last sublist item to the path. */
227             last_item_path = libvlc_media_list_path_copy_by_appending(current_item, i_count - 1);
228             free(current_item);
229             /* Check that sublist item for more descendants. */
230             last_item_path = find_last_item(p_mlist, last_item_path);
231         }
232
233         libvlc_media_list_release(p_sublist);
234     }
235
236     return last_item_path;
237 }
238
239 /**************************************************************************
240  *       get_previous_path (private)
241  *
242  *  Returns the path to the preceding item in the list.
243  *  If looping is specified and the current item is the first list item in
244  *  the list it will return the last descendant of the last item in the list.
245  **************************************************************************/
246 static libvlc_media_list_path_t
247 get_previous_path(libvlc_media_list_player_t * p_mlp, bool b_loop)
248 {
249     assert_locked(p_mlp);
250
251     /* We are entered with libvlc_media_list_lock(p_mlp->p_list) */
252     libvlc_media_list_path_t ret;
253     libvlc_media_list_t * p_parent_of_playing_item;
254
255     if (!p_mlp->current_playing_item_path)
256     {
257         if (!libvlc_media_list_count(p_mlp->p_mlist))
258             return NULL;
259         return libvlc_media_list_path_with_root_index(0);
260     }
261
262     /* Try to catch parent element */
263     p_parent_of_playing_item = libvlc_media_list_parentlist_at_path(
264                                             p_mlp->p_mlist,
265                                             p_mlp->current_playing_item_path);
266
267     int depth = libvlc_media_list_path_depth(p_mlp->current_playing_item_path);
268     if (depth < 1 || !p_parent_of_playing_item)
269         return NULL;
270
271     /* Set the return path to the current path */
272     ret = libvlc_media_list_path_copy(p_mlp->current_playing_item_path);
273
274     /* Change the return path to the previous list entry */
275     ret[depth - 1]--; /* set to previous element */
276     ret[depth] = -1;
277
278     /* Is the return path is beyond the start of the current list? */
279     if(ret[depth - 1] < 0)
280     {
281         /* Move to parent of current item */
282         depth--;
283
284         /* Are we at the root level of the tree? */
285         if (depth <= 0)
286         {
287             // Is looping enabled?
288             if(b_loop)
289             {
290                 int i_count = libvlc_media_list_count(p_parent_of_playing_item);
291
292                 /* Set current play item to the last element in the list */
293                 ret[0] = i_count - 1;
294                 ret[1] = -1;
295
296                 /* Set the return path to the last descendant item of the current item */
297                 ret = find_last_item(p_mlp->p_mlist, ret);
298             }
299             else
300             {
301                 /* No looping so return empty path. */
302                 free(ret);
303                 ret = NULL;
304             }
305         }
306         else
307         {
308             /* This is the case of moving backward from the beginning of the
309             *  subitem list to its parent item.
310             *  This ensures that current path is properly terminated to
311             *  use that parent.
312             */
313             ret[depth] = -1;
314         }
315     }
316     else
317     {
318         ret = find_last_item(p_mlp->p_mlist, ret);
319     }
320
321     libvlc_media_list_release(p_parent_of_playing_item);
322     return ret;
323 }
324
325 static void *playlist_thread(void *data)
326 {
327     libvlc_media_list_player_t *mlp = data;
328
329     vlc_mutex_lock(&mlp->mp_callback_lock);
330     mutex_cleanup_push(&mlp->mp_callback_lock);
331
332     for (;;)
333     {
334         int canc;
335
336         while (mlp->seek_offset == 0)
337             vlc_cond_wait(&mlp->seek_pending, &mlp->mp_callback_lock);
338
339         canc = vlc_savecancel();
340         set_relative_playlist_position_and_play(mlp, mlp->seek_offset);
341         mlp->seek_offset = 0;
342         vlc_restorecancel(canc);
343     }
344
345     vlc_cleanup_pop();
346     vlc_assert_unreachable();
347 }
348
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     /* This event is triggered from the input thread, and changing item in
359      * the media player requires the input thread to terminate. So we cannot
360      * change the playlist state here (it would cause a deadlock). Instead, we
361      * defer to a separate thread. Avoiding this would be nice... */
362     vlc_mutex_lock(&p_mlp->mp_callback_lock);
363     p_mlp->seek_offset++;
364     vlc_cond_signal(&p_mlp->seek_pending);
365     vlc_mutex_unlock(&p_mlp->mp_callback_lock);
366 }
367
368 /**************************************************************************
369  *       playlist_item_deleted (private) (Event Callback)
370  **************************************************************************/
371 static void
372 mlist_item_deleted(const libvlc_event_t * p_event, void * p_user_data)
373 {
374     // Nothing to do. For now.
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     assert_locked(p_mlp);
396     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     assert_locked(p_mlp);
418
419     // Allow callbacks to run, because detach() will wait until all callbacks are processed.
420     // This is safe because only callbacks are allowed, and there execution will be cancelled.
421     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     // Now, lock back the callback lock. No more callback will be present from this point.
425     vlc_mutex_lock(&p_mlp->mp_callback_lock);
426
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  * Playlist lock should be held
435  **************************************************************************/
436 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);
440
441     /* First, save the new path that we are going to play */
442     if (p_mlp->current_playing_item_path != path)
443     {
444         free(p_mlp->current_playing_item_path);
445         p_mlp->current_playing_item_path = path;
446     }
447
448     if (!path)
449         return;
450
451     libvlc_media_t * p_md;
452     p_md = libvlc_media_list_item_at_path(p_mlp->p_mlist, path);
453     if (!p_md)
454         return;
455
456     /* Make sure media_player_reached_end() won't get called */
457     uninstall_media_player_observer(p_mlp);
458
459     libvlc_media_player_set_media(p_mlp->p_mi, p_md);
460
461     install_media_player_observer(p_mlp);
462     libvlc_media_release(p_md); /* for libvlc_media_list_item_at_index */
463 }
464
465 /*
466  * Public libvlc functions
467  */
468
469 /**************************************************************************
470  *         new (Public)
471  **************************************************************************/
472 libvlc_media_list_player_t *
473 libvlc_media_list_player_new(libvlc_instance_t * p_instance)
474 {
475     libvlc_media_list_player_t * p_mlp;
476     p_mlp = calloc( 1, sizeof(libvlc_media_list_player_t) );
477     if (unlikely(p_mlp == NULL))
478     {
479         libvlc_printerr("Not enough memory");
480         return NULL;
481     }
482
483     p_mlp->i_refcount = 1;
484     p_mlp->seek_offset = 0;
485     vlc_mutex_init(&p_mlp->object_lock);
486     vlc_mutex_init(&p_mlp->mp_callback_lock);
487     vlc_cond_init(&p_mlp->seek_pending);
488
489     p_mlp->p_event_manager = libvlc_event_manager_new(p_mlp);
490     if (unlikely(p_mlp->p_event_manager == NULL))
491         goto error;
492
493     /* Create the underlying media_player */
494     p_mlp->p_mi = libvlc_media_player_new(p_instance);
495     if( p_mlp->p_mi == NULL )
496     {
497         libvlc_event_manager_release(p_mlp->p_event_manager);
498         goto error;
499     }
500     install_media_player_observer(p_mlp);
501
502     if (vlc_clone(&p_mlp->thread, playlist_thread, p_mlp,
503                   VLC_THREAD_PRIORITY_LOW))
504     {
505         libvlc_media_player_release(p_mlp->p_mi);
506         libvlc_event_manager_release(p_mlp->p_event_manager);
507         goto error;
508     }
509
510     return p_mlp;
511 error:
512     vlc_cond_destroy(&p_mlp->seek_pending);
513     vlc_mutex_destroy(&p_mlp->mp_callback_lock);
514     vlc_mutex_destroy(&p_mlp->object_lock);
515     free(p_mlp);
516     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         return;
526
527     lock(p_mlp);
528     p_mlp->i_refcount--;
529     if (p_mlp->i_refcount > 0)
530     {
531         unlock(p_mlp);
532         return;
533     }
534     assert(p_mlp->i_refcount == 0);
535     unlock(p_mlp);
536
537     vlc_cancel(p_mlp->thread);
538     vlc_join(p_mlp->thread, NULL);
539
540     lock(p_mlp);
541     /* Keep the lock(), because the uninstall functions
542      * check for it. That's convenient. */
543     uninstall_media_player_observer(p_mlp);
544     libvlc_media_player_release(p_mlp->p_mi);
545
546     if (p_mlp->p_mlist)
547     {
548         uninstall_playlist_observer(p_mlp);
549         libvlc_media_list_release(p_mlp->p_mlist);
550     }
551
552     unlock(p_mlp);
553
554     libvlc_event_manager_release(p_mlp->p_event_manager);
555     vlc_cond_destroy(&p_mlp->seek_pending);
556     vlc_mutex_destroy(&p_mlp->mp_callback_lock);
557     vlc_mutex_destroy(&p_mlp->object_lock);
558
559     free(p_mlp->current_playing_item_path);
560     free(p_mlp);
561 }
562
563 /**************************************************************************
564  *        retain (Public)
565  **************************************************************************/
566 void libvlc_media_list_player_retain(libvlc_media_list_player_t * p_mlp)
567 {
568     if (!p_mlp)
569         return;
570
571     lock(p_mlp);
572     p_mlp->i_refcount++;
573     unlock(p_mlp);
574 }
575
576 /**************************************************************************
577  *        event_manager (Public)
578  **************************************************************************/
579 libvlc_event_manager_t *
580 libvlc_media_list_player_event_manager(libvlc_media_list_player_t * p_mlp)
581 {
582     return p_mlp->p_event_manager;
583 }
584
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     lock(p_mlp);
596     uninstall_media_player_observer(p_mlp);
597     p_oldmi = p_mlp->p_mi;
598     p_mlp->p_mi = p_mi;
599     install_media_player_observer(p_mlp);
600     unlock(p_mlp);
601
602     libvlc_media_player_release(p_oldmi);
603 }
604
605 /**************************************************************************
606  *        get_media_player (Public)
607  **************************************************************************/
608 libvlc_media_player_t * libvlc_media_list_player_get_media_player(libvlc_media_list_player_t * p_mlp)
609 {
610     libvlc_media_player_retain(p_mlp->p_mi);
611     return p_mlp->p_mi;
612 }
613
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);
620
621     lock(p_mlp);
622     if (p_mlp->p_mlist)
623     {
624         uninstall_playlist_observer(p_mlp);
625         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  *        Play (Public)
637  **************************************************************************/
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  *        Pause (Public)
654  **************************************************************************/
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  *        is_playing (Public)
664  **************************************************************************/
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     return (state == libvlc_Opening) || (state == libvlc_Playing);
670 }
671
672 /**************************************************************************
673  *        State (Public)
674  **************************************************************************/
675 libvlc_state_t
676 libvlc_media_list_player_get_state(libvlc_media_list_player_t * p_mlp)
677 {
678     return libvlc_media_player_get_state(p_mlp->p_mi);
679 }
680
681 /**************************************************************************
682  *        Play item at index (Public)
683  **************************************************************************/
684 int libvlc_media_list_player_play_item_at_index(libvlc_media_list_player_t * p_mlp, int i_index)
685 {
686     lock(p_mlp);
687     libvlc_media_list_path_t path = libvlc_media_list_path_with_root_index(i_index);
688     set_current_playing_item(p_mlp, path);
689     libvlc_media_t *p_md = libvlc_media_player_get_media(p_mlp->p_mi);
690     libvlc_media_player_play(p_mlp->p_mi);
691     unlock(p_mlp);
692
693     if (!p_md)
694         return -1;
695
696     /* Send the next item event */
697     libvlc_event_t event;
698     event.type = libvlc_MediaListPlayerNextItemSet;
699     event.u.media_list_player_next_item_set.item = p_md;
700     libvlc_event_send(p_mlp->p_event_manager, &event);
701     libvlc_media_release(p_md);
702     return 0;
703 }
704
705 /**************************************************************************
706  *        Play item (Public)
707  **************************************************************************/
708 int libvlc_media_list_player_play_item(libvlc_media_list_player_t * p_mlp, libvlc_media_t * p_md)
709 {
710     lock(p_mlp);
711     libvlc_media_list_path_t path = libvlc_media_list_path_of_item(p_mlp->p_mlist, p_md);
712     if (!path)
713     {
714         libvlc_printerr("Item not found in media list");
715         unlock(p_mlp);
716         return -1;
717     }
718
719     set_current_playing_item(p_mlp, path);
720     libvlc_media_player_play(p_mlp->p_mi);
721     unlock(p_mlp);
722     return 0;
723 }
724
725 /**************************************************************************
726  *       Stop (Private)
727  *
728  * Lock must be held.
729  **************************************************************************/
730 static void stop(libvlc_media_list_player_t * p_mlp)
731 {
732     assert_locked(p_mlp);
733
734     /* We are not interested in getting media stop event now */
735     uninstall_media_player_observer(p_mlp);
736     libvlc_media_player_stop(p_mlp->p_mi);
737     install_media_player_observer(p_mlp);
738
739     free(p_mlp->current_playing_item_path);
740     p_mlp->current_playing_item_path = NULL;
741
742     /* Send the event */
743     libvlc_event_t event;
744     event.type = libvlc_MediaListPlayerStopped;
745     libvlc_event_send(p_mlp->p_event_manager, &event);
746 }
747
748 /**************************************************************************
749  *       Stop (Public)
750  **************************************************************************/
751 void libvlc_media_list_player_stop(libvlc_media_list_player_t * p_mlp)
752 {
753     lock(p_mlp);
754     stop(p_mlp);
755     unlock(p_mlp);
756 }
757
758 /**************************************************************************
759  *       Set relative playlist position and play (Private)
760  *
761  * Sets the currently played item to the given relative play item position
762  * (based on the currently playing item) and then begins the new item playback.
763  * Lock must be held.
764  **************************************************************************/
765 static int set_relative_playlist_position_and_play(
766                                       libvlc_media_list_player_t * p_mlp,
767                                       int i_relative_position)
768 {
769     assert_locked(p_mlp);
770
771     if (!p_mlp->p_mlist)
772     {
773         libvlc_printerr("No media list");
774         return -1;
775     }
776
777     libvlc_media_list_lock(p_mlp->p_mlist);
778
779     libvlc_media_list_path_t path = p_mlp->current_playing_item_path;
780
781     if(p_mlp->e_playback_mode != libvlc_playback_mode_repeat)
782     {
783         bool b_loop = (p_mlp->e_playback_mode == libvlc_playback_mode_loop);
784
785         while (i_relative_position > 0)
786         {
787             path = get_next_path(p_mlp, b_loop);
788             set_current_playing_item(p_mlp, path);
789             --i_relative_position;
790         }
791
792         while (i_relative_position < 0)
793         {
794             path = get_previous_path(p_mlp, b_loop);
795             set_current_playing_item(p_mlp, path);
796             ++i_relative_position;
797         }
798     }
799     else
800     {
801         set_current_playing_item(p_mlp, path);
802     }
803
804 #ifdef DEBUG_MEDIA_LIST_PLAYER
805     printf("Playing:");
806     libvlc_media_list_path_dump(path);
807 #endif
808
809     if (!path)
810     {
811         libvlc_media_list_unlock(p_mlp->p_mlist);
812         /* Send list played event */
813         libvlc_event_t event;
814         event.type = libvlc_MediaListPlayerPlayed;
815         libvlc_event_send(p_mlp->p_event_manager, &event);
816         return -1;
817     }
818
819     libvlc_media_player_play(p_mlp->p_mi);
820
821     libvlc_media_list_unlock(p_mlp->p_mlist);
822
823     /* Send the next item event */
824     libvlc_event_t event;
825     event.type = libvlc_MediaListPlayerNextItemSet;
826     libvlc_media_t * p_md = libvlc_media_list_item_at_path(p_mlp->p_mlist, path);
827     event.u.media_list_player_next_item_set.item = p_md;
828     libvlc_event_send(p_mlp->p_event_manager, &event);
829     libvlc_media_release(p_md);
830     return 0;
831 }
832
833 /**************************************************************************
834  *       Next (Public)
835  **************************************************************************/
836 int libvlc_media_list_player_next(libvlc_media_list_player_t * p_mlp)
837 {
838     lock(p_mlp);
839     int failure = set_relative_playlist_position_and_play(p_mlp, 1);
840     unlock(p_mlp);
841     return failure;
842 }
843
844 /**************************************************************************
845  *       Previous (Public)
846  **************************************************************************/
847 int libvlc_media_list_player_previous(libvlc_media_list_player_t * p_mlp)
848 {
849     lock(p_mlp);
850     int failure = set_relative_playlist_position_and_play(p_mlp, -1);
851     unlock(p_mlp);
852     return failure;
853 }
854
855 /**************************************************************************
856  *       Set Playback Mode (Public)
857  **************************************************************************/
858 void libvlc_media_list_player_set_playback_mode(
859                                             libvlc_media_list_player_t * p_mlp,
860                                             libvlc_playback_mode_t e_mode )
861 {
862     lock(p_mlp);
863     p_mlp->e_playback_mode = e_mode;
864     unlock(p_mlp);
865 }