]> git.sesse.net Git - vlc/blob - src/control/media_list_player.c
Move LibVLC error messages to libvlc_printerr().
[vlc] / src / control / media_list_player.c
1 /*****************************************************************************
2  * media_list_player.c: libvlc new API media_list player functions
3  *****************************************************************************
4  * Copyright (C) 2007 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Pierre d'Herbemont <pdherbemont # videolan.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 #include <vlc/libvlc.h>
29 #include <vlc/libvlc_media.h>
30 #include <vlc/libvlc_media_list.h>
31 #include <vlc/libvlc_media_player.h>
32 #include <vlc/libvlc_media_list_player.h>
33 #include <vlc/libvlc_events.h>
34 #include <assert.h>
35
36 #include "libvlc_internal.h"
37
38 #include "media_internal.h" // Abuse, could and should be removed
39 #include "media_list_path.h"
40
41 //#define DEBUG_MEDIA_LIST_PLAYER
42
43 /* This is a very dummy implementation of playlist on top of
44  * media_list and media_player.
45  *
46  * All this code is doing is simply computing the next item
47  * of a tree of media_list (see get_next_index()), and play
48  * the next item when the current is over. This is happening
49  * via the event callback media_player_reached_end().
50  *
51  * This is thread safe, and we use a two keys (locks) scheme
52  * to discriminate between callbacks and regular uses.
53  */
54
55 struct libvlc_media_list_player_t
56 {
57     libvlc_event_manager_t *    p_event_manager;
58     libvlc_instance_t *         p_libvlc_instance;
59     int                         i_refcount;
60     /* Protect access to this structure. */
61     vlc_mutex_t                 object_lock;
62     /* Protect access to this structure and from callback execution. */
63     vlc_mutex_t                 mp_callback_lock;
64     /* Indicate to media player callbacks that they are cancelled. */
65     bool                        are_mp_callback_cancelled;
66     libvlc_media_list_path_t    current_playing_item_path;
67     libvlc_media_t *            p_current_playing_item;
68     libvlc_media_list_t *       p_mlist;
69     libvlc_media_player_t *     p_mi;
70     libvlc_playback_mode_t      e_playback_mode;
71 };
72
73 /* This is not yet exported by libvlccore */
74 static inline void vlc_assert_locked(vlc_mutex_t *mutex)
75 {
76     VLC_UNUSED(mutex);
77 }
78
79 /*
80  * Forward declaration
81  */
82
83 static void set_relative_playlist_position_and_play(
84                                             libvlc_media_list_player_t * p_mlp, 
85                                             int i_relative_position, 
86                                             libvlc_exception_t * p_e);
87 static void stop(libvlc_media_list_player_t * p_mlp, libvlc_exception_t * p_e);
88
89 /*
90  * Private functions
91  */
92
93 /**************************************************************************
94  * Shortcuts
95  **************************************************************************/
96 static inline void lock(libvlc_media_list_player_t * p_mlp)
97 {
98     // Obtain an access to this structure
99     vlc_mutex_lock(&p_mlp->object_lock);
100     
101     // Make sure no callback will occurs at the same time
102     vlc_mutex_lock(&p_mlp->mp_callback_lock);
103 }
104
105 static inline void unlock(libvlc_media_list_player_t * p_mlp)
106 {
107     vlc_mutex_unlock(&p_mlp->mp_callback_lock);
108     vlc_mutex_unlock(&p_mlp->object_lock);
109 }
110
111 static inline void assert_locked(libvlc_media_list_player_t * p_mlp)
112 {
113     vlc_assert_locked(&p_mlp->mp_callback_lock);
114 }
115
116 static inline libvlc_event_manager_t * mlist_em(libvlc_media_list_player_t * p_mlp)
117 {
118     assert_locked(p_mlp);
119     return libvlc_media_list_event_manager(p_mlp->p_mlist, NULL);
120 }
121
122 static inline libvlc_event_manager_t * mplayer_em(libvlc_media_list_player_t * p_mlp)
123 {
124     assert_locked(p_mlp);
125     return libvlc_media_player_event_manager(p_mlp->p_mi, NULL);
126 }
127
128 /**************************************************************************
129  *       get_next_path (private)
130  *
131  *  Returns the path to the next item in the list.  
132  *  If looping is specified and the current item is the last list item in
133  *  the list it will return the first item in the list.
134  **************************************************************************/
135 static libvlc_media_list_path_t
136 get_next_path(libvlc_media_list_player_t * p_mlp, bool b_loop)
137 {
138     assert_locked(p_mlp);
139
140     /* We are entered with libvlc_media_list_lock(p_mlp->p_list) */
141     libvlc_media_list_path_t ret;
142     libvlc_media_list_t * p_parent_of_playing_item;
143     libvlc_media_list_t * p_sublist_of_playing_item;
144
145     if (!p_mlp->current_playing_item_path)
146     {
147         if (!libvlc_media_list_count(p_mlp->p_mlist, NULL))
148             return NULL;
149         return libvlc_media_list_path_with_root_index(0);
150     }
151     
152     p_sublist_of_playing_item = libvlc_media_list_sublist_at_path(
153                             p_mlp->p_mlist,
154                             p_mlp->current_playing_item_path);
155  
156     /* If item just gained a sublist just play it */
157     if (p_sublist_of_playing_item)
158     {
159         libvlc_media_list_release(p_sublist_of_playing_item);
160         return libvlc_media_list_path_copy_by_appending(p_mlp->current_playing_item_path, 0);
161     }
162
163     /* Try to catch parent element */
164     p_parent_of_playing_item = libvlc_media_list_parentlist_at_path(p_mlp->p_mlist,
165                             p_mlp->current_playing_item_path);
166
167     int depth = libvlc_media_list_path_depth(p_mlp->current_playing_item_path);
168     if (depth < 1 || !p_parent_of_playing_item)
169         return NULL;
170
171     ret = libvlc_media_list_path_copy(p_mlp->current_playing_item_path);
172     ret[depth - 1]++; /* set to next element */
173
174     /* If this goes beyond the end of the list */
175     while(ret[depth-1] >= libvlc_media_list_count(p_parent_of_playing_item, NULL))
176     {
177         depth--;
178         if (depth <= 0)
179         {
180             if(b_loop)
181             {
182                 ret[0] = 0;
183                 ret[1] = -1;
184                 break;
185             }
186             else
187             {
188                 free(ret);
189                 libvlc_media_list_release(p_parent_of_playing_item);
190                 return NULL;
191             }
192         }
193         ret[depth] = -1;
194         ret[depth-1]++;
195         p_parent_of_playing_item  = libvlc_media_list_parentlist_at_path(
196                                         p_mlp->p_mlist,
197                                         ret);
198     }
199
200     libvlc_media_list_release(p_parent_of_playing_item);
201     return ret;
202 }
203
204 /**************************************************************************
205  *       find_last_item (private)
206  *
207  *  Returns the path of the last descendant of a given item path.
208  *  Note: Due to the recursive nature of the function and the need to free
209  *        media list paths, paths passed in may be freed if they are replaced.
210           Recommended usage is to set return value to the same path that was 
211           passed to the function (i.e. item = find_last_item(list, item); )
212  **************************************************************************/
213 static libvlc_media_list_path_t
214 find_last_item( libvlc_media_list_t * p_mlist, libvlc_media_list_path_t current_item )
215 {
216     libvlc_media_list_t * p_sublist = libvlc_media_list_sublist_at_path(p_mlist, current_item);
217     libvlc_media_list_path_t last_item_path = current_item;
218
219     if(p_sublist)
220     {
221         int i_count = libvlc_media_list_count(p_sublist, NULL);
222         if(i_count > 0)
223         {
224             /* Add the last sublist item to the path. */
225             last_item_path = libvlc_media_list_path_copy_by_appending(current_item, i_count - 1);
226             free(current_item);
227             /* Check that sublist item for more descendants. */
228             last_item_path = find_last_item(p_mlist, last_item_path);
229         }
230
231         libvlc_media_list_release(p_sublist);
232     }    
233
234     return last_item_path;
235 }
236
237 /**************************************************************************
238  *       get_previous_path (private)
239  *
240  *  Returns the path to the preceding item in the list.  
241  *  If looping is specified and the current item is the first list item in
242  *  the list it will return the last descendant of the last item in the list.
243  **************************************************************************/
244 static libvlc_media_list_path_t
245 get_previous_path(libvlc_media_list_player_t * p_mlp, bool b_loop)
246 {
247     assert_locked(p_mlp);
248
249     /* We are entered with libvlc_media_list_lock(p_mlp->p_list) */
250     libvlc_media_list_path_t ret;
251     libvlc_media_list_t * p_parent_of_playing_item;
252
253     if (!p_mlp->current_playing_item_path)
254     {
255         if (!libvlc_media_list_count(p_mlp->p_mlist, NULL))
256             return NULL;
257         return libvlc_media_list_path_with_root_index(0);
258     }
259     
260     /* Try to catch parent element */
261     p_parent_of_playing_item = libvlc_media_list_parentlist_at_path(
262                                             p_mlp->p_mlist,
263                                             p_mlp->current_playing_item_path);
264
265     int depth = libvlc_media_list_path_depth(p_mlp->current_playing_item_path);
266     if (depth < 1 || !p_parent_of_playing_item)
267         return NULL;
268
269     /* Set the return path to the current path */
270     ret = libvlc_media_list_path_copy(p_mlp->current_playing_item_path);
271
272     /* Change the return path to the previous list entry */
273     ret[depth - 1]--; /* set to previous element */
274     ret[depth] = -1;
275
276     /* Is the return path is beyond the start of the current list? */
277     if(ret[depth - 1] < 0)
278     {
279         /* Move to parent of current item */
280         depth--;
281
282         /* Are we at the root level of the tree? */
283         if (depth <= 0)
284         {
285             // Is looping enabled?
286             if(b_loop)
287             {
288                 int i_count = libvlc_media_list_count(p_parent_of_playing_item, NULL);
289
290                 /* Set current play item to the last element in the list */
291                 ret[0] = i_count - 1;
292                 ret[1] = -1;
293
294                 /* Set the return path to the last descendant item of the current item */
295                 ret = find_last_item(p_mlp->p_mlist, ret);
296             }
297             else
298             {
299                 /* No looping so return empty path. */
300                 free(ret);
301                 ret = NULL;
302             }
303         }
304         else
305         {
306             /* This is the case of moving backward from the beginning of the
307             *  subitem list to its parent item.
308             *  This ensures that current path is properly terminated to
309             *  use that parent.
310             */
311             ret[depth] = -1;
312         }
313     }
314     else
315     {
316         ret = find_last_item(p_mlp->p_mlist, ret);
317     }
318
319     libvlc_media_list_release(p_parent_of_playing_item);
320     return ret;
321 }
322
323 /**************************************************************************
324  *       media_player_reached_end (private) (Event Callback)
325  **************************************************************************/
326 static void
327 media_player_reached_end(const libvlc_event_t * p_event, void * p_user_data)
328 {
329     VLC_UNUSED(p_event);
330     libvlc_media_list_player_t * p_mlp = p_user_data;
331     libvlc_exception_t e;
332     libvlc_exception_init(&e);
333
334     vlc_mutex_lock(&p_mlp->mp_callback_lock);
335     if (!p_mlp->are_mp_callback_cancelled)
336         set_relative_playlist_position_and_play(p_mlp, 1, &e);
337     vlc_mutex_unlock(&p_mlp->mp_callback_lock);
338
339     // There is no point in reporting an error from this callback
340     libvlc_exception_clear(&e);
341 }
342
343 /**************************************************************************
344  *       playlist_item_deleted (private) (Event Callback)
345  **************************************************************************/
346 static void
347 mlist_item_deleted(const libvlc_event_t * p_event, void * p_user_data)
348 {
349     libvlc_media_list_player_t * p_mlp = p_user_data;
350     libvlc_media_list_t * p_emitting_mlist = p_event->p_obj;
351     libvlc_media_t * p_current_md = NULL;
352
353     lock(p_mlp);
354     if (p_mlp->current_playing_item_path)
355         p_current_md = libvlc_media_list_item_at_path(p_mlp->p_mlist, p_mlp->current_playing_item_path);
356
357     if (p_event->u.media_list_item_deleted.item == p_current_md &&
358         p_emitting_mlist == p_mlp->p_mlist)
359     {
360         /* We are playing this item, let's stop */
361         stop(p_mlp, NULL);
362     }
363     unlock(p_mlp);
364 }
365
366
367 /**************************************************************************
368  * install_playlist_observer (private)
369  **************************************************************************/
370 static void
371 install_playlist_observer(libvlc_media_list_player_t * p_mlp)
372 {
373     assert_locked(p_mlp);
374     libvlc_event_attach(mlist_em(p_mlp), libvlc_MediaListItemDeleted, mlist_item_deleted, p_mlp, NULL);
375 }
376
377 /**************************************************************************
378  * uninstall_playlist_observer (private)
379  **************************************************************************/
380 static void
381 uninstall_playlist_observer(libvlc_media_list_player_t * p_mlp)
382 {
383     assert_locked(p_mlp);
384     if (!p_mlp->p_mlist) return;
385     libvlc_event_detach(mlist_em(p_mlp), libvlc_MediaListItemDeleted, mlist_item_deleted, p_mlp, NULL);
386 }
387
388 /**************************************************************************
389  * install_media_player_observer (private)
390  **************************************************************************/
391 static void
392 install_media_player_observer(libvlc_media_list_player_t * p_mlp)
393 {
394     assert_locked(p_mlp);
395     libvlc_event_attach_async(mplayer_em(p_mlp), libvlc_MediaPlayerEndReached, media_player_reached_end, p_mlp, NULL);
396 }
397
398
399 /**************************************************************************
400  *       uninstall_media_player_observer (private)
401  **************************************************************************/
402 static void
403 uninstall_media_player_observer(libvlc_media_list_player_t * p_mlp)
404 {
405     assert_locked(p_mlp);
406     if (!p_mlp->p_mi) return;
407
408     // From now on, media_player callback won't be relevant.
409     p_mlp->are_mp_callback_cancelled = true;
410
411     // Allow callbacks to run, because detach() will wait until all callbacks are processed.
412     // This is safe because only callbacks are allowed, and there execution will be cancelled.
413     vlc_mutex_unlock(&p_mlp->mp_callback_lock);
414     libvlc_event_detach(mplayer_em(p_mlp), libvlc_MediaPlayerEndReached, media_player_reached_end, p_mlp, NULL);
415
416     // Now, lock back the callback lock. No more callback will be present from this point.
417     vlc_mutex_lock(&p_mlp->mp_callback_lock);
418     p_mlp->are_mp_callback_cancelled = false;
419
420     // What is here is safe, because we guarantee that we won't be able to anything concurrently,
421     // - except (cancelled) callbacks - thanks to the object_lock.
422 }
423
424 /**************************************************************************
425  *       set_current_playing_item (private)
426  *
427  * Playlist lock should be held
428  **************************************************************************/
429 static void
430 set_current_playing_item(libvlc_media_list_player_t * p_mlp, libvlc_media_list_path_t path)
431 {
432     assert_locked(p_mlp);
433
434     /* First, save the new path that we are going to play */
435     if (p_mlp->current_playing_item_path != path)
436     {
437         free(p_mlp->current_playing_item_path);
438         p_mlp->current_playing_item_path = path;
439     }
440
441     if (!path)
442         return;
443
444     libvlc_media_t * p_md;
445     p_md = libvlc_media_list_item_at_path(p_mlp->p_mlist, path);
446     if (!p_md)
447         return;
448     
449     /* Make sure media_player_reached_end() won't get called */
450     uninstall_media_player_observer(p_mlp);
451
452     /* Create a new media_player if there is none */
453     if (!p_mlp->p_mi)
454         p_mlp->p_mi = libvlc_media_player_new_from_media(p_md, NULL);
455
456     libvlc_media_player_set_media(p_mlp->p_mi, p_md, NULL);
457
458     install_media_player_observer(p_mlp);
459     libvlc_media_release(p_md); /* for libvlc_media_list_item_at_index */
460 }
461
462 /*
463  * Public libvlc functions
464  */
465
466 /**************************************************************************
467  *         new (Public)
468  **************************************************************************/
469 libvlc_media_list_player_t *
470 libvlc_media_list_player_new(libvlc_instance_t * p_instance, libvlc_exception_t * p_e)
471 {
472     (void)p_e;
473     libvlc_media_list_player_t * p_mlp;
474     p_mlp = calloc( 1, sizeof(libvlc_media_list_player_t) );
475     if (!p_mlp)
476         return NULL;
477
478     libvlc_retain(p_instance);
479     p_mlp->p_libvlc_instance = p_instance;
480     p_mlp->i_refcount = 1;
481     vlc_mutex_init(&p_mlp->object_lock);
482     vlc_mutex_init(&p_mlp->mp_callback_lock);
483     p_mlp->p_event_manager = libvlc_event_manager_new(p_mlp, p_instance, p_e);
484     libvlc_event_manager_register_event_type(p_mlp->p_event_manager, libvlc_MediaListPlayerNextItemSet, p_e);
485     p_mlp->e_playback_mode = libvlc_playback_mode_default;
486
487     return p_mlp;
488 }
489
490 /**************************************************************************
491  *         release (Public)
492  **************************************************************************/
493 void libvlc_media_list_player_release(libvlc_media_list_player_t * p_mlp)
494 {
495     if (!p_mlp)
496         return;
497
498     lock(p_mlp);
499     p_mlp->i_refcount--;
500     if (p_mlp->i_refcount > 0)
501     {
502         unlock(p_mlp);
503         return;
504     }
505
506     assert(p_mlp->i_refcount == 0);
507
508     /* Keep the lock(), because the uninstall functions
509      * check for it. That's convenient. */
510
511     if (p_mlp->p_mi)
512     {
513         uninstall_media_player_observer(p_mlp);
514         libvlc_media_player_release(p_mlp->p_mi);
515     }    
516     if (p_mlp->p_mlist)
517     {
518         uninstall_playlist_observer(p_mlp);
519         libvlc_media_list_release(p_mlp->p_mlist);
520     }
521
522     unlock(p_mlp);
523     vlc_mutex_destroy(&p_mlp->object_lock);
524     vlc_mutex_destroy(&p_mlp->mp_callback_lock);
525
526     libvlc_event_manager_release(p_mlp->p_event_manager);
527     
528     free(p_mlp->current_playing_item_path);
529     libvlc_release(p_mlp->p_libvlc_instance);
530     free(p_mlp);
531 }
532
533 /**************************************************************************
534  *        event_manager (Public)
535  **************************************************************************/
536 libvlc_event_manager_t *
537 libvlc_media_list_player_event_manager(libvlc_media_list_player_t * p_mlp)
538 {
539     return p_mlp->p_event_manager;
540 }
541
542 /**************************************************************************
543  *        set_media_player (Public)
544  **************************************************************************/
545 void libvlc_media_list_player_set_media_player(libvlc_media_list_player_t * p_mlp, libvlc_media_player_t * p_mi, libvlc_exception_t * p_e)
546 {
547     VLC_UNUSED(p_e);
548
549     lock(p_mlp);
550
551     if (p_mlp->p_mi)
552     {
553         uninstall_media_player_observer(p_mlp);
554         libvlc_media_player_release(p_mlp->p_mi);
555     }
556     libvlc_media_player_retain(p_mi);
557     p_mlp->p_mi = p_mi;
558
559     install_media_player_observer(p_mlp);
560
561     unlock(p_mlp);
562 }
563
564 /**************************************************************************
565  *       set_media_list (Public)
566  **************************************************************************/
567 void libvlc_media_list_player_set_media_list(libvlc_media_list_player_t * p_mlp, libvlc_media_list_t * p_mlist, libvlc_exception_t * p_e)
568 {
569     assert (p_mlist);
570
571     lock(p_mlp);
572     if (p_mlp->p_mlist)
573     {
574         uninstall_playlist_observer(p_mlp);
575         libvlc_media_list_release(p_mlp->p_mlist);
576     }
577     libvlc_media_list_retain(p_mlist);
578     p_mlp->p_mlist = p_mlist;
579  
580     install_playlist_observer(p_mlp);
581
582     unlock(p_mlp);
583 }
584
585 /**************************************************************************
586  *        Play (Public)
587  **************************************************************************/
588 void libvlc_media_list_player_play(libvlc_media_list_player_t * p_mlp, libvlc_exception_t * p_e)
589 {
590     lock(p_mlp);
591     if (!p_mlp->current_playing_item_path)
592     {
593         set_relative_playlist_position_and_play(p_mlp, 1, p_e);
594         unlock(p_mlp);
595         return; /* Will set to play */
596     }
597     libvlc_media_player_play(p_mlp->p_mi, p_e);
598     unlock(p_mlp);
599 }
600
601
602 /**************************************************************************
603  *        Pause (Public)
604  **************************************************************************/
605 void libvlc_media_list_player_pause(libvlc_media_list_player_t * p_mlp, libvlc_exception_t * p_e)
606 {
607     lock(p_mlp);
608     if (!p_mlp->p_mi)
609     {
610         unlock(p_mlp);
611         return;
612     }
613     libvlc_media_player_pause(p_mlp->p_mi, p_e);
614     unlock(p_mlp);
615 }
616
617 /**************************************************************************
618  *        is_playing (Public)
619  **************************************************************************/
620 int
621 libvlc_media_list_player_is_playing(libvlc_media_list_player_t * p_mlp, libvlc_exception_t * p_e)
622 {
623     libvlc_state_t state = libvlc_media_player_get_state(p_mlp->p_mi, p_e);
624     return (state == libvlc_Opening) || (state == libvlc_Buffering) ||
625            (state == libvlc_Playing);
626 }
627
628 /**************************************************************************
629  *        State (Public)
630  **************************************************************************/
631 libvlc_state_t
632 libvlc_media_list_player_get_state(libvlc_media_list_player_t * p_mlp, libvlc_exception_t * p_e)
633 {
634     if (!p_mlp->p_mi)
635         return libvlc_Ended;
636     return libvlc_media_player_get_state(p_mlp->p_mi, p_e);
637 }
638
639 /**************************************************************************
640  *        Play item at index (Public)
641  **************************************************************************/
642 void libvlc_media_list_player_play_item_at_index(libvlc_media_list_player_t * p_mlp, int i_index, libvlc_exception_t * p_e)
643 {
644     VLC_UNUSED(p_e);
645
646     lock(p_mlp);
647     set_current_playing_item(p_mlp, libvlc_media_list_path_with_root_index(i_index));
648     libvlc_media_player_play(p_mlp->p_mi, p_e);
649     unlock(p_mlp);
650
651     /* Send the next item event */
652     libvlc_event_t event;
653     event.type = libvlc_MediaListPlayerNextItemSet;
654     libvlc_event_send(p_mlp->p_event_manager, &event);
655 }
656
657 /**************************************************************************
658  *        Play item (Public)
659  **************************************************************************/
660 void libvlc_media_list_player_play_item(libvlc_media_list_player_t * p_mlp, libvlc_media_t * p_md, libvlc_exception_t * p_e)
661 {
662     lock(p_mlp);
663     libvlc_media_list_path_t path = libvlc_media_list_path_of_item(p_mlp->p_mlist, p_md);
664     if (!path)
665     {
666         libvlc_exception_raise(p_e);
667         libvlc_printerr("Item not found in media list");
668         unlock(p_mlp);
669         return;
670     }
671     
672     set_current_playing_item(p_mlp, path);
673     libvlc_media_player_play(p_mlp->p_mi, p_e);
674     unlock(p_mlp);
675 }
676
677 /**************************************************************************
678  *       Stop (Private)
679  *
680  * Lock must be held.
681  **************************************************************************/
682 static void stop(libvlc_media_list_player_t * p_mlp, libvlc_exception_t * p_e)
683 {
684     assert_locked(p_mlp);
685
686     if (p_mlp->p_mi && p_mlp->current_playing_item_path)
687     {
688         /* We are not interested in getting media stop event now */
689         uninstall_media_player_observer(p_mlp);
690         libvlc_media_player_stop(p_mlp->p_mi, p_e);
691         install_media_player_observer(p_mlp);
692     }
693
694     free(p_mlp->current_playing_item_path);
695     p_mlp->current_playing_item_path = NULL;
696 }
697
698 /**************************************************************************
699  *       Stop (Public)
700  **************************************************************************/
701 void libvlc_media_list_player_stop(libvlc_media_list_player_t * p_mlp, 
702                                    libvlc_exception_t * p_e)
703 {
704     lock(p_mlp);
705     stop(p_mlp, p_e);
706     unlock(p_mlp);
707 }
708
709 /**************************************************************************
710  *       Set relative playlist position and play (Private)
711  *
712  * Sets the currently played item to the given relative play item position 
713  * (based on the currently playing item) and then begins the new item playback.
714  * Lock must be held.  
715  **************************************************************************/
716 static void set_relative_playlist_position_and_play(
717                                       libvlc_media_list_player_t * p_mlp, 
718                                       int i_relative_position, 
719                                       libvlc_exception_t * p_e)
720 {
721     assert_locked(p_mlp);
722
723     if (!p_mlp->p_mlist)
724     {
725         libvlc_exception_raise(p_e);
726         libvlc_printerr("No media list");
727         return;
728     }
729
730     libvlc_media_list_lock(p_mlp->p_mlist);
731
732     libvlc_media_list_path_t path = p_mlp->current_playing_item_path;
733
734     if(p_mlp->e_playback_mode != libvlc_playback_mode_repeat)
735     {
736         bool b_loop = (p_mlp->e_playback_mode == libvlc_playback_mode_loop);
737         
738         if(i_relative_position > 0)
739         {
740             do
741             {
742                 path = get_next_path(p_mlp, b_loop);
743                 set_current_playing_item(p_mlp, path);
744                 --i_relative_position;
745             }
746             while(i_relative_position > 0);
747         }
748         else if(i_relative_position < 0)
749         {
750             do 
751             {
752                 path = get_previous_path(p_mlp, b_loop);
753                 set_current_playing_item(p_mlp, path);
754                 ++i_relative_position;
755             } 
756             while (i_relative_position < 0);
757         }
758     }
759     else
760     {
761         set_current_playing_item(p_mlp, path);
762     }
763
764 #ifdef DEBUG_MEDIA_LIST_PLAYER
765     printf("Playing:");
766     libvlc_media_list_path_dump(path);
767 #endif
768
769     if (!path)
770     {
771         libvlc_media_list_unlock(p_mlp->p_mlist);
772         stop(p_mlp, p_e);
773         return;
774     }
775
776     libvlc_media_player_play(p_mlp->p_mi, p_e);
777
778     libvlc_media_list_unlock(p_mlp->p_mlist);
779
780     /* Send the next item event */
781     libvlc_event_t event;
782     event.type = libvlc_MediaListPlayerNextItemSet;
783     libvlc_media_t * p_md = libvlc_media_list_item_at_path(p_mlp->p_mlist, path);
784     event.u.media_list_player_next_item_set.item = p_md;
785     libvlc_event_send(p_mlp->p_event_manager, &event);
786     libvlc_media_release(p_md);
787 }
788
789 /**************************************************************************
790  *       Next (Public)
791  **************************************************************************/
792 void libvlc_media_list_player_next(libvlc_media_list_player_t * p_mlp, 
793                                    libvlc_exception_t * p_e)
794 {
795     lock(p_mlp);
796     set_relative_playlist_position_and_play(p_mlp, 1, p_e);
797     unlock(p_mlp);
798 }
799
800 /**************************************************************************
801  *       Previous (Public)
802  **************************************************************************/
803 void libvlc_media_list_player_previous(libvlc_media_list_player_t * p_mlp, 
804                                        libvlc_exception_t * p_e)
805 {
806     lock(p_mlp);
807     set_relative_playlist_position_and_play(p_mlp, -1, p_e);
808     unlock(p_mlp);
809 }
810
811 /**************************************************************************
812  *       Set Playback Mode (Public)
813  **************************************************************************/
814 void libvlc_media_list_player_set_playback_mode( 
815                                             libvlc_media_list_player_t * p_mlp,
816                                             libvlc_playback_mode_t e_mode,
817                                             libvlc_exception_t * p_e )
818 {
819     VLC_UNUSED(p_e);
820
821     lock(p_mlp);
822     p_mlp->e_playback_mode = e_mode;
823     unlock(p_mlp);
824 }