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