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