Main Page | Modules | Class Hierarchy | Class List | Directories | File List | Class Members | File Members | Related Pages

playlist.c

00001 /*****************************************************************************
00002  * playlist.c : Playlist management functions
00003  *****************************************************************************
00004  * Copyright (C) 1999-2004 the VideoLAN team
00005  * $Id: playlist.c 12842 2005-10-15 17:55:46Z asmax $
00006  *
00007  * Authors: Samuel Hocevar <[email protected]>
00008  *          Clément Stenac <[email protected]>
00009  *
00010  * This program is free software; you can redistribute it and/or modify
00011  * it under the terms of the GNU General Public License as published by
00012  * the Free Software Foundation; either version 2 of the License, or
00013  * (at your option) any later version.
00014  *
00015  * This program is distributed in the hope that it will be useful,
00016  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018  * GNU General Public License for more details.
00019  *
00020  * You should have received a copy of the GNU General Public License
00021  * along with this program; if not, write to the Free Software
00022  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
00023  *****************************************************************************/
00024 #include <stdlib.h>                                      /* free(), strtol() */
00025 #include <stdio.h>                                              /* sprintf() */
00026 #include <string.h>                                            /* strerror() */
00027 
00028 #include <vlc/vlc.h>
00029 #include <vlc/vout.h>
00030 #include <vlc/sout.h>
00031 #include <vlc/input.h>
00032 
00033 #include "vlc_playlist.h"
00034 
00035 #define TITLE_CATEGORY N_( "By category" )
00036 #define TITLE_SIMPLE   N_( "Manually added" )
00037 #define TITLE_ALL      N_( "All items, unsorted" )
00038 
00039 #undef PLAYLIST_PROFILE
00040 #undef PLAYLIST_DEBUG
00041 
00042 /*****************************************************************************
00043  * Local prototypes
00044  *****************************************************************************/
00045 static void RunThread ( playlist_t * );
00046 static void RunPreparse( playlist_preparse_t * );
00047 static playlist_item_t * NextItem  ( playlist_t * );
00048 static int PlayItem  ( playlist_t *, playlist_item_t * );
00049 
00050 static int ItemChange( vlc_object_t *, const char *,
00051                        vlc_value_t, vlc_value_t, void * );
00052 
00053 int playlist_vaControl( playlist_t * p_playlist, int i_query, va_list args );
00054 
00055 void playlist_PreparseEnqueueItemSub( playlist_t *, playlist_item_t * );
00056 
00057 playlist_item_t *playlist_RecursiveFindLast(playlist_t *p_playlist,
00058                                             playlist_item_t *p_node );
00059 
00060 /*****************************************************************************
00061  * Helper Function for NextItem
00062  *****************************************************************************/
00063 
00064 playlist_item_t *playlist_RecursiveFindLast(playlist_t *p_playlist,
00065                                             playlist_item_t *p_node )
00066 {
00067     int i;
00068     playlist_item_t *p_item;
00069     for ( i = p_node->i_children - 1; i >= 0; i-- )
00070     {
00071         if( p_node->pp_children[i]->i_children == -1 )
00072             return p_node->pp_children[i];
00073         else if(p_node->pp_children[i]->i_children > 0)
00074         {
00075              p_item = playlist_RecursiveFindLast( p_playlist,
00076                                         p_node->pp_children[i] );
00077             if ( p_item != NULL )
00078                 return p_item;
00079         }
00080         else if( i == 0 )
00081             return NULL;
00082     }
00083     return NULL;
00084 }
00085 
00086 
00094 playlist_t * __playlist_Create ( vlc_object_t *p_parent )
00095 {
00096     playlist_t *p_playlist;
00097     playlist_view_t *p_view;
00098     vlc_value_t     val;
00099 
00100     /* Allocate structure */
00101     p_playlist = vlc_object_create( p_parent, VLC_OBJECT_PLAYLIST );
00102     if( !p_playlist )
00103     {
00104         msg_Err( p_parent, "out of memory" );
00105         return NULL;
00106     }
00107 
00108     /* These variables control updates */
00109     var_Create( p_playlist, "intf-change", VLC_VAR_BOOL );
00110     val.b_bool = VLC_TRUE;
00111     var_Set( p_playlist, "intf-change", val );
00112 
00113     var_Create( p_playlist, "item-change", VLC_VAR_INTEGER );
00114     val.i_int = -1;
00115     var_Set( p_playlist, "item-change", val );
00116 
00117     var_Create( p_playlist, "item-deleted", VLC_VAR_INTEGER );
00118     val.i_int = -1;
00119     var_Set( p_playlist, "item-deleted", val );
00120 
00121     var_Create( p_playlist, "item-append", VLC_VAR_ADDRESS );
00122 
00123     var_Create( p_playlist, "playlist-current", VLC_VAR_INTEGER );
00124     val.i_int = -1;
00125     var_Set( p_playlist, "playlist-current", val );
00126 
00127     var_Create( p_playlist, "intf-popupmenu", VLC_VAR_BOOL );
00128 
00129     var_Create( p_playlist, "intf-show", VLC_VAR_BOOL );
00130     val.b_bool = VLC_TRUE;
00131     var_Set( p_playlist, "intf-show", val );
00132 
00133 
00134     /* Variables to control playback */
00135     var_CreateGetBool( p_playlist, "play-and-stop" );
00136     var_CreateGetBool( p_playlist, "random" );
00137     var_CreateGetBool( p_playlist, "repeat" );
00138     var_CreateGetBool( p_playlist, "loop" );
00139 
00140     /* Initialise data structures */
00141     vlc_mutex_init( p_playlist, &p_playlist->gc_lock );
00142     p_playlist->i_last_id = 0;
00143     p_playlist->b_go_next = VLC_TRUE;
00144     p_playlist->p_input = NULL;
00145 
00146     p_playlist->request_date = 0;
00147 
00148     p_playlist->i_views = 0;
00149     p_playlist->pp_views = NULL;
00150 
00151     p_playlist->i_index = -1;
00152     p_playlist->i_size = 0;
00153     p_playlist->pp_items = NULL;
00154     p_playlist->i_all_size = 0;
00155     p_playlist->pp_all_items = 0;
00156 
00157     playlist_ViewInsert( p_playlist, VIEW_CATEGORY, TITLE_CATEGORY );
00158     playlist_ViewInsert( p_playlist, VIEW_ALL, TITLE_ALL );
00159 
00160     p_view = playlist_ViewFind( p_playlist, VIEW_CATEGORY );
00161 
00162     p_playlist->p_general =
00163         playlist_NodeCreate( p_playlist, VIEW_CATEGORY,
00164                              _( "General" ), p_view->p_root );
00165     p_playlist->p_general->i_flags |= PLAYLIST_RO_FLAG;
00166 
00167     /* Set startup status
00168      * We set to simple view on startup for interfaces that don't do
00169      * anything */
00170     p_view = playlist_ViewFind( p_playlist, VIEW_CATEGORY );
00171     p_playlist->status.i_view = VIEW_CATEGORY;
00172     p_playlist->status.p_item = NULL;
00173     p_playlist->status.p_node = p_view->p_root;
00174     p_playlist->request.b_request = VLC_FALSE;
00175     p_playlist->status.i_status = PLAYLIST_STOPPED;
00176 
00177 
00178     p_playlist->i_sort = SORT_ID;
00179     p_playlist->i_order = ORDER_NORMAL;
00180 
00181     /* Finally, launch the thread ! */
00182     if( vlc_thread_create( p_playlist, "playlist", RunThread,
00183                            VLC_THREAD_PRIORITY_LOW, VLC_TRUE ) )
00184     {
00185         msg_Err( p_playlist, "cannot spawn playlist thread" );
00186         vlc_object_destroy( p_playlist );
00187         return NULL;
00188     }
00189 
00190     /* Preparsing stuff */
00191     p_playlist->p_preparse = vlc_object_create( p_playlist,
00192                                                 sizeof( playlist_preparse_t ) );
00193     if( !p_playlist->p_preparse )
00194     {
00195         msg_Err( p_playlist, "unable to create preparser" );
00196         vlc_object_destroy( p_playlist );
00197         return NULL;
00198     }
00199 
00200     p_playlist->p_preparse->i_waiting = 0;
00201     p_playlist->p_preparse->pp_waiting = NULL;
00202 
00203     vlc_object_attach( p_playlist->p_preparse, p_playlist );
00204     if( vlc_thread_create( p_playlist->p_preparse, "preparser",
00205                            RunPreparse, VLC_THREAD_PRIORITY_LOW, VLC_TRUE ) )
00206     {
00207         msg_Err( p_playlist, "cannot spawn preparse thread" );
00208         vlc_object_detach( p_playlist->p_preparse );
00209         vlc_object_destroy( p_playlist->p_preparse );
00210         return NULL;
00211     }
00212 
00213     /* The object has been initialized, now attach it */
00214     vlc_object_attach( p_playlist, p_parent );
00215 
00216     return p_playlist;
00217 }
00218 
00226 int playlist_Destroy( playlist_t * p_playlist )
00227 {
00228     int i;
00229     p_playlist->b_die = 1;
00230 
00231     while( p_playlist->i_sds )
00232     {
00233         playlist_ServicesDiscoveryRemove( p_playlist,
00234                                           p_playlist->pp_sds[0]->psz_module );
00235     }
00236 
00237     vlc_thread_join( p_playlist->p_preparse );
00238     vlc_thread_join( p_playlist );
00239 
00240     vlc_object_detach( p_playlist->p_preparse );
00241 
00242     var_Destroy( p_playlist, "intf-change" );
00243     var_Destroy( p_playlist, "item-change" );
00244     var_Destroy( p_playlist, "playlist-current" );
00245     var_Destroy( p_playlist, "intf-popmenu" );
00246     var_Destroy( p_playlist, "intf-show" );
00247     var_Destroy( p_playlist, "play-and-stop" );
00248     var_Destroy( p_playlist, "random" );
00249     var_Destroy( p_playlist, "repeat" );
00250     var_Destroy( p_playlist, "loop" );
00251 
00252     playlist_Clear( p_playlist );
00253 
00254     for( i = p_playlist->i_views - 1; i >= 0 ; i-- )
00255     {
00256         playlist_view_t *p_view = p_playlist->pp_views[i];
00257         if( p_view->psz_name )
00258             free( p_view->psz_name );
00259         playlist_ItemDelete( p_view->p_root );
00260         REMOVE_ELEM( p_playlist->pp_views, p_playlist->i_views, i );
00261         free( p_view );
00262     }
00263 
00264     vlc_mutex_destroy( &p_playlist->gc_lock );
00265     vlc_object_destroy( p_playlist->p_preparse );
00266     vlc_object_destroy( p_playlist );
00267 
00268     return VLC_SUCCESS;
00269 }
00270 
00271 
00284 int playlist_LockControl( playlist_t * p_playlist, int i_query, ... )
00285 {
00286     va_list args;
00287     int i_result;
00288     va_start( args, i_query );
00289     vlc_mutex_lock( &p_playlist->object_lock );
00290     i_result = playlist_vaControl( p_playlist, i_query, args );
00291     va_end( args );
00292     vlc_mutex_unlock( &p_playlist->object_lock );
00293     return i_result;
00294 }
00295 
00308 int playlist_Control( playlist_t * p_playlist, int i_query, ... )
00309 {
00310     va_list args;
00311     int i_result;
00312     va_start( args, i_query );
00313     i_result = playlist_vaControl( p_playlist, i_query, args );
00314     va_end( args );
00315 
00316     return i_result;
00317 }
00318 
00319 int playlist_vaControl( playlist_t * p_playlist, int i_query, va_list args )
00320 {
00321     playlist_view_t *p_view;
00322     playlist_item_t *p_item, *p_node;
00323     int i_view;
00324     vlc_value_t val;
00325 
00326 #ifdef PLAYLIST_PROFILE
00327     p_playlist->request_date = mdate();
00328 #endif
00329 
00330     if( p_playlist->i_size <= 0 )
00331     {
00332         return VLC_EGENERIC;
00333     }
00334 
00335     switch( i_query )
00336     {
00337     case PLAYLIST_STOP:
00338         p_playlist->status.i_status = PLAYLIST_STOPPED;
00339         p_playlist->request.b_request = VLC_TRUE;
00340         p_playlist->request.p_item = NULL;
00341         break;
00342 
00343     case PLAYLIST_ITEMPLAY:
00344         p_item = (playlist_item_t *)va_arg( args, playlist_item_t * );
00345         if ( p_item == NULL || p_item->input.psz_uri == NULL )
00346             return VLC_EGENERIC;
00347         p_playlist->status.i_status = PLAYLIST_RUNNING;
00348         p_playlist->request.i_skip = 0;
00349         p_playlist->request.b_request = VLC_TRUE;
00350         p_playlist->request.p_item = p_item;
00351         p_playlist->request.i_view = p_playlist->status.i_view;
00352         p_view = playlist_ViewFind( p_playlist, p_playlist->status.i_view );
00353         if( p_view )
00354         {
00355             p_playlist->request.p_node = p_view->p_root;
00356         }
00357         else
00358         {
00359             p_playlist->request.p_node = NULL;
00360         }
00361         break;
00362 
00363     case PLAYLIST_VIEWPLAY:
00364         i_view = (int)va_arg( args,int );
00365         p_node = (playlist_item_t *)va_arg( args, playlist_item_t * );
00366         p_item = (playlist_item_t *)va_arg( args, playlist_item_t * );
00367         if ( p_node == NULL || (p_item != NULL && p_item->input.psz_uri
00368                                                          == NULL ))
00369         {
00370             p_playlist->status.i_status = PLAYLIST_STOPPED;
00371             p_playlist->request.b_request = VLC_TRUE;
00372             return VLC_SUCCESS;
00373         }
00374         p_playlist->status.i_status = PLAYLIST_RUNNING;
00375         p_playlist->request.i_skip = 0;
00376         p_playlist->request.b_request = VLC_TRUE;
00377         p_playlist->request.i_view = i_view;
00378         p_playlist->request.p_node = p_node;
00379         p_playlist->request.p_item = p_item;
00380 
00381         /* If we select a node, play only it.
00382          * If we select an item, continue */
00383         if( p_playlist->request.p_item == NULL ||
00384             ! p_playlist->request.p_node->i_flags & PLAYLIST_SKIP_FLAG )
00385         {
00386             p_playlist->b_go_next = VLC_FALSE;
00387         }
00388         else
00389         {
00390             p_playlist->b_go_next = VLC_TRUE;
00391         }
00392         break;
00393 
00394     case PLAYLIST_PLAY:
00395         p_playlist->status.i_status = PLAYLIST_RUNNING;
00396 
00397         if( p_playlist->p_input )
00398         {
00399             val.i_int = PLAYING_S;
00400             var_Set( p_playlist->p_input, "state", val );
00401             break;
00402         }
00403 
00404         /* FIXME : needed ? */
00405         p_playlist->request.b_request = VLC_TRUE;
00406         p_playlist->request.i_view = p_playlist->status.i_view;
00407         p_playlist->request.p_node = p_playlist->status.p_node;
00408         p_playlist->request.p_item = p_playlist->status.p_item;
00409         p_playlist->request.i_skip = 0;
00410         p_playlist->request.i_goto = -1;
00411         break;
00412 
00413     case PLAYLIST_AUTOPLAY:
00414         p_playlist->status.i_status = PLAYLIST_RUNNING;
00415         p_playlist->status.p_node = p_playlist->p_general;
00416 
00417         p_playlist->request.b_request = VLC_FALSE;
00418         break;
00419 
00420     case PLAYLIST_PAUSE:
00421         val.i_int = 0;
00422         if( p_playlist->p_input )
00423             var_Get( p_playlist->p_input, "state", &val );
00424 
00425         if( val.i_int == PAUSE_S )
00426         {
00427             p_playlist->status.i_status = PLAYLIST_RUNNING;
00428             if( p_playlist->p_input )
00429             {
00430                 val.i_int = PLAYING_S;
00431                 var_Set( p_playlist->p_input, "state", val );
00432             }
00433         }
00434         else
00435         {
00436             p_playlist->status.i_status = PLAYLIST_PAUSED;
00437             if( p_playlist->p_input )
00438             {
00439                 val.i_int = PAUSE_S;
00440                 var_Set( p_playlist->p_input, "state", val );
00441             }
00442         }
00443         break;
00444 
00445     case PLAYLIST_SKIP:
00446         p_playlist->request.i_view = p_playlist->status.i_view;
00447         if( p_playlist->status.i_view > -1 )
00448         {
00449             p_playlist->request.p_node = p_playlist->status.p_node;
00450             p_playlist->request.p_item = p_playlist->status.p_item;
00451         }
00452         else
00453         {
00454             p_playlist->request.p_node = NULL;
00455             p_playlist->request.p_item = NULL;
00456         }
00457         p_playlist->request.i_skip = (int) va_arg( args, int );
00458         p_playlist->request.b_request = VLC_TRUE;
00459         break;
00460 
00461     case PLAYLIST_GOTO:
00462         p_playlist->status.i_status = PLAYLIST_RUNNING;
00463         p_playlist->request.p_node = NULL;
00464         p_playlist->request.p_item = NULL;
00465         p_playlist->request.i_view = -1;
00466         p_playlist->request.i_goto = (int) va_arg( args, int );
00467         p_playlist->request.b_request = VLC_TRUE;
00468         break;
00469 
00470     default:
00471         msg_Err( p_playlist, "unimplemented playlist query" );
00472         return VLC_EBADVAR;
00473         break;
00474     }
00475 
00476     return VLC_SUCCESS;
00477 }
00478 
00479 int playlist_PreparseEnqueue( playlist_t *p_playlist,
00480                               input_item_t *p_item )
00481 {
00482     vlc_mutex_lock( &p_playlist->p_preparse->object_lock );
00483     INSERT_ELEM( p_playlist->p_preparse->pp_waiting,
00484                  p_playlist->p_preparse->i_waiting,
00485                  p_playlist->p_preparse->i_waiting,
00486                  p_item );
00487     vlc_mutex_unlock( &p_playlist->p_preparse->object_lock );
00488     return VLC_SUCCESS;
00489 }
00490 
00491 /* Should only be called if playlist and preparser are locked */
00492 void playlist_PreparseEnqueueItemSub( playlist_t *p_playlist,
00493                                      playlist_item_t *p_item )
00494 {
00495     int i;
00496     if( p_item->i_children == -1 )
00497     {
00498         INSERT_ELEM( p_playlist->p_preparse->pp_waiting,
00499                      p_playlist->p_preparse->i_waiting,
00500                      p_playlist->p_preparse->i_waiting,
00501                      &(p_item->input) );
00502     }
00503     else
00504     {
00505         for( i = 0; i < p_item->i_children; i++)
00506         {
00507             playlist_PreparseEnqueueItemSub( p_playlist,
00508                                              p_item->pp_children[i] );
00509         }
00510     }
00511 }
00512 
00513 int playlist_PreparseEnqueueItem( playlist_t *p_playlist,
00514                                   playlist_item_t *p_item )
00515 {
00516     vlc_mutex_lock( &p_playlist->object_lock );
00517     vlc_mutex_lock( &p_playlist->p_preparse->object_lock );
00518     playlist_PreparseEnqueueItemSub( p_playlist, p_item );
00519     vlc_mutex_unlock( &p_playlist->p_preparse->object_lock );
00520     vlc_mutex_unlock( &p_playlist->object_lock );
00521     return VLC_SUCCESS;
00522 }
00523 
00524 
00525 /* Destroy remaining objects */
00526 static mtime_t ObjectGarbageCollector( playlist_t *p_playlist, int i_type,
00527                                        mtime_t destroy_date )
00528 {
00529     vlc_object_t *p_obj;
00530 
00531     if( destroy_date > mdate() ) return destroy_date;
00532 
00533     if( destroy_date == 0 )
00534     {
00535         /* give a little time */
00536         return mdate() + I64C(1000000);
00537     }
00538     else
00539     {
00540         vlc_mutex_lock( &p_playlist->gc_lock );
00541         while( ( p_obj = vlc_object_find( p_playlist, i_type, FIND_CHILD ) ) )
00542         {
00543             if( p_obj->p_parent != (vlc_object_t*)p_playlist )
00544             {
00545                 /* only first child (ie unused) */
00546                 vlc_object_release( p_obj );
00547                 break;
00548             }
00549             if( i_type == VLC_OBJECT_VOUT )
00550             {
00551                 msg_Dbg( p_playlist, "garbage collector destroying 1 vout" );
00552                 vlc_object_detach( p_obj );
00553                 vlc_object_release( p_obj );
00554                 vout_Destroy( (vout_thread_t *)p_obj );
00555             }
00556             else if( i_type == VLC_OBJECT_SOUT )
00557             {
00558                 vlc_object_release( p_obj );
00559                 sout_DeleteInstance( (sout_instance_t*)p_obj );
00560             }
00561         }
00562         vlc_mutex_unlock( &p_playlist->gc_lock );
00563         return 0;
00564     }
00565 }
00566 
00567 /*****************************************************************************
00568  * RunThread: main playlist thread
00569  *****************************************************************************/
00570 static void RunThread ( playlist_t *p_playlist )
00571 {
00572     vlc_object_t *p_obj;
00573     playlist_item_t *p_item = NULL;
00574 
00575     mtime_t    i_vout_destroyed_date = 0;
00576     mtime_t    i_sout_destroyed_date = 0;
00577 
00578     playlist_item_t *p_autodelete_item = NULL;
00579 
00580     /* Tell above that we're ready */
00581     vlc_thread_ready( p_playlist );
00582 
00583     while( !p_playlist->b_die )
00584     {
00585         vlc_mutex_lock( &p_playlist->object_lock );
00586 
00587         /* First, check if we have something to do */
00588         /* FIXME : this can be called several times */
00589         if( p_playlist->request.b_request )
00590         {
00591 #ifdef PLAYLIST_PROFILE
00592             msg_Dbg(p_playlist, "beginning processing of request, "
00593                          I64Fi" us ", mdate() - p_playlist->request_date );
00594 #endif
00595             /* Stop the existing input */
00596             if( p_playlist->p_input )
00597             {
00598                 input_StopThread( p_playlist->p_input );
00599             }
00600             /* The code below will start the next input for us */
00601             if( p_playlist->status.i_status == PLAYLIST_STOPPED )
00602             {
00603                 p_playlist->request.b_request = VLC_FALSE;
00604             }
00605         }
00606 
00607         /* If there is an input, check that it doesn't need to die. */
00608         if( p_playlist->p_input )
00609         {
00610             /* This input is dead. Remove it ! */
00611             if( p_playlist->p_input->b_dead )
00612             {
00613                 input_thread_t *p_input;
00614 
00615                 p_input = p_playlist->p_input;
00616                 p_playlist->p_input = NULL;
00617 
00618                 /* Release the playlist lock, because we may get stuck
00619                  * in input_DestroyThread() for some time. */
00620                 vlc_mutex_unlock( &p_playlist->object_lock );
00621 
00622                 /* Destroy input */
00623                 input_DestroyThread( p_input );
00624 
00625                 /* Unlink current input
00626                  * (_after_ input_DestroyThread for vout garbage collector) */
00627                 vlc_object_detach( p_input );
00628 
00629                 /* Destroy object */
00630                 vlc_object_destroy( p_input );
00631 
00632                 i_vout_destroyed_date = 0;
00633                 i_sout_destroyed_date = 0;
00634 
00635                 if( p_playlist->status.p_item->i_flags
00636                     & PLAYLIST_REMOVE_FLAG )
00637                 {
00638                      playlist_ItemDelete( p_item );
00639                      p_playlist->status.p_item = NULL;
00640                 }
00641 
00642                 continue;
00643             }
00644             /* This input is dying, let it do */
00645             else if( p_playlist->p_input->b_die )
00646             {
00647                 ;
00648             }
00649             /* This input has finished, ask it to die ! */
00650             else if( p_playlist->p_input->b_error
00651                       || p_playlist->p_input->b_eof )
00652             {
00653                 /* TODO FIXME XXX TODO FIXME XXX */
00654                 /* Check for autodeletion */
00655 
00656                 if( p_playlist->status.p_item->i_flags & PLAYLIST_DEL_FLAG )
00657                 {
00658                     p_autodelete_item = p_playlist->status.p_item;
00659                 }
00660                 input_StopThread( p_playlist->p_input );
00661                 /* Select the next playlist item */
00662                 vlc_mutex_unlock( &p_playlist->object_lock );
00663                 continue;
00664             }
00665             else if( p_playlist->p_input->i_state != INIT_S )
00666             {
00667                 vlc_mutex_unlock( &p_playlist->object_lock );
00668                 i_vout_destroyed_date =
00669                     ObjectGarbageCollector( p_playlist, VLC_OBJECT_VOUT,
00670                                             i_vout_destroyed_date );
00671                 i_sout_destroyed_date =
00672                     ObjectGarbageCollector( p_playlist, VLC_OBJECT_SOUT,
00673                                             i_sout_destroyed_date );
00674                 vlc_mutex_lock( &p_playlist->object_lock );
00675             }
00676         }
00677         else if( p_playlist->status.i_status != PLAYLIST_STOPPED )
00678         {
00679             /* Start another input.
00680              * Get the next item to play */
00681             p_item = NextItem( p_playlist );
00682 
00683 
00684             /* We must stop */
00685             if( p_item == NULL )
00686             {
00687                 if( p_autodelete_item )
00688                 {
00689                     playlist_Delete( p_playlist,
00690                                      p_autodelete_item->input.i_id );
00691                     p_autodelete_item = NULL;
00692                 }
00693                 p_playlist->status.i_status = PLAYLIST_STOPPED;
00694                 vlc_mutex_unlock( &p_playlist->object_lock );
00695                 continue;
00696             }
00697 
00698             PlayItem( p_playlist, p_item );
00699 
00700             if( p_autodelete_item )
00701             {
00702                 playlist_Delete( p_playlist, p_autodelete_item->input.i_id );
00703                 p_autodelete_item = NULL;
00704             }
00705         }
00706         else if( p_playlist->status.i_status == PLAYLIST_STOPPED )
00707         {
00708             if( p_item && p_playlist->status.p_item &&
00709                 p_playlist->status.p_item->i_flags & PLAYLIST_REMOVE_FLAG )
00710             {
00711                  playlist_ItemDelete( p_item );
00712                  p_playlist->status.p_item = NULL;
00713             }
00714 
00715             /* Collect garbage */
00716             vlc_mutex_unlock( &p_playlist->object_lock );
00717             i_sout_destroyed_date =
00718                 ObjectGarbageCollector( p_playlist, VLC_OBJECT_SOUT, mdate() );
00719             i_vout_destroyed_date =
00720                 ObjectGarbageCollector( p_playlist, VLC_OBJECT_VOUT, mdate() );
00721             vlc_mutex_lock( &p_playlist->object_lock );
00722         }
00723         vlc_mutex_unlock( &p_playlist->object_lock );
00724 
00725         msleep( INTF_IDLE_SLEEP / 2 );
00726 
00727         /* Stop sleeping earlier if we have work */
00728         /* TODO : statistics about this */
00729         if ( p_playlist->request.b_request &&
00730                         p_playlist->status.i_status == PLAYLIST_RUNNING )
00731         {
00732             continue;
00733         }
00734 
00735         msleep( INTF_IDLE_SLEEP / 2 );
00736     }
00737 
00738     /* Playlist dying */
00739 
00740     /* If there is an input, kill it */
00741     while( 1 )
00742     {
00743         vlc_mutex_lock( &p_playlist->object_lock );
00744 
00745         if( p_playlist->p_input == NULL )
00746         {
00747             vlc_mutex_unlock( &p_playlist->object_lock );
00748             break;
00749         }
00750 
00751         if( p_playlist->p_input->b_dead )
00752         {
00753             input_thread_t *p_input;
00754 
00755             /* Unlink current input */
00756             p_input = p_playlist->p_input;
00757             p_playlist->p_input = NULL;
00758             vlc_mutex_unlock( &p_playlist->object_lock );
00759 
00760             /* Destroy input */
00761             input_DestroyThread( p_input );
00762             /* Unlink current input (_after_ input_DestroyThread for vout
00763              * garbage collector)*/
00764             vlc_object_detach( p_input );
00765 
00766             /* Destroy object */
00767             vlc_object_destroy( p_input );
00768             continue;
00769         }
00770         else if( p_playlist->p_input->b_die )
00771         {
00772             /* This input is dying, leave it alone */
00773             ;
00774         }
00775         else if( p_playlist->p_input->b_error || p_playlist->p_input->b_eof )
00776         {
00777             input_StopThread( p_playlist->p_input );
00778             vlc_mutex_unlock( &p_playlist->object_lock );
00779             continue;
00780         }
00781         else
00782         {
00783             p_playlist->p_input->b_eof = 1;
00784         }
00785 
00786         vlc_mutex_unlock( &p_playlist->object_lock );
00787 
00788         msleep( INTF_IDLE_SLEEP );
00789     }
00790 
00791     /* close all remaining sout */
00792     while( ( p_obj = vlc_object_find( p_playlist,
00793                                       VLC_OBJECT_SOUT, FIND_CHILD ) ) )
00794     {
00795         vlc_object_release( p_obj );
00796         sout_DeleteInstance( (sout_instance_t*)p_obj );
00797     }
00798 
00799     /* close all remaining vout */
00800     while( ( p_obj = vlc_object_find( p_playlist,
00801                                       VLC_OBJECT_VOUT, FIND_CHILD ) ) )
00802     {
00803         vlc_object_detach( p_obj );
00804         vlc_object_release( p_obj );
00805         vout_Destroy( (vout_thread_t *)p_obj );
00806     }
00807 }
00808 
00809 /* Queue for items to preparse */
00810 static void RunPreparse ( playlist_preparse_t *p_obj )
00811 {
00812     playlist_t *p_playlist = (playlist_t *)p_obj->p_parent;
00813     vlc_bool_t b_sleep;
00814 
00815     /* Tell above that we're ready */
00816     vlc_thread_ready( p_obj );
00817 
00818     while( !p_playlist->b_die )
00819     {
00820         vlc_mutex_lock( &p_obj->object_lock );
00821 
00822         if( p_obj->i_waiting > 0 )
00823         {
00824             input_item_t *p_current = p_obj->pp_waiting[0];
00825             REMOVE_ELEM( p_obj->pp_waiting, p_obj->i_waiting, 0 );
00826             vlc_mutex_unlock( &p_obj->object_lock );
00827             input_Preparse( p_playlist, p_current );
00828             var_SetInteger( p_playlist, "item-change", p_current->i_id );
00829             vlc_mutex_lock( &p_obj->object_lock );
00830         }
00831         b_sleep = ( p_obj->i_waiting == 0 );
00832 
00833         vlc_mutex_unlock( &p_obj->object_lock );
00834 
00835         if( p_obj->i_waiting == 0 )
00836         {
00837             msleep( INTF_IDLE_SLEEP );
00838         }
00839     }
00840 }
00841 
00842 /*****************************************************************************
00843  * NextItem
00844  *****************************************************************************
00845  * This function calculates the next playlist item, depending
00846  * on the playlist course mode (forward, backward, random, view,...).
00847  *****************************************************************************/
00848 static playlist_item_t * NextItem( playlist_t *p_playlist )
00849 {
00850     playlist_item_t *p_new = NULL;
00851     int i_skip,i_goto,i, i_new, i_count ;
00852     playlist_view_t *p_view;
00853 
00854     vlc_bool_t b_loop = var_GetBool( p_playlist, "loop" );
00855     vlc_bool_t b_random = var_GetBool( p_playlist, "random" );
00856     vlc_bool_t b_repeat = var_GetBool( p_playlist, "repeat" );
00857     vlc_bool_t b_playstop = var_GetBool( p_playlist, "play-and-stop" );
00858 
00859 #ifdef PLAYLIST_PROFILE
00860     /* Calculate time needed */
00861     int64_t start = mdate();
00862 #endif
00863 
00864     /* Handle quickly a few special cases */
00865 
00866     /* No items to play */
00867     if( p_playlist->i_size == 0 )
00868     {
00869         msg_Info( p_playlist, "playlist is empty" );
00870         return NULL;
00871     }
00872     /* Nothing requested */
00873     if( !p_playlist->request.b_request && p_playlist->status.p_item == NULL )
00874     {
00875         msg_Dbg( p_playlist,"nothing requested, starting" );
00876     }
00877 
00878     /* Repeat and play/stop */
00879     if( !p_playlist->request.b_request && b_repeat == VLC_TRUE )
00880     {
00881         msg_Dbg( p_playlist,"repeating item" );
00882         return p_playlist->status.p_item;
00883     }
00884 
00885     if( !p_playlist->request.b_request && b_playstop == VLC_TRUE )
00886     {
00887         msg_Dbg( p_playlist,"stopping (play and stop)");
00888         return NULL;
00889     }
00890 
00891     if( !p_playlist->request.b_request && p_playlist->status.p_item &&
00892         !( p_playlist->status.p_item->i_flags & PLAYLIST_SKIP_FLAG ) )
00893     {
00894         msg_Dbg( p_playlist, "no-skip mode, stopping") ;
00895         return NULL;
00896     }
00897 
00898     /* TODO: improve this (only use current node) */
00899     /* TODO: use the "shuffled view" internally ? */
00900     /* Random case. This is an exception: if request, but request is skip +- 1
00901      * we don't go to next item but select a new random one. */
00902     if( b_random && 
00903         ( !p_playlist->request.b_request ||
00904         ( p_playlist->request.b_request && ( p_playlist->request.p_item == NULL ||
00905           p_playlist->request.i_skip == 1 || p_playlist->request.i_skip == -1 ) ) ) )
00906     {
00907         /* how many items to choose from ? */
00908         i_count = 0;
00909         for ( i = 0; i < p_playlist->i_size; i++ )
00910         {
00911             if ( p_playlist->pp_items[i]->i_nb_played == 0 )
00912                 i_count++;
00913         }
00914         /* Nothing left? */
00915         if ( i_count == 0 )
00916         {
00917             /* Don't loop? Exit! */
00918             if( !b_loop )
00919                 return NULL;
00920             /* Otherwise reset the counter */
00921             for ( i = 0; i < p_playlist->i_size; i++ )
00922             {
00923                 p_playlist->pp_items[i]->i_nb_played = 0;
00924             }
00925             i_count = p_playlist->i_size;
00926         }
00927         srand( (unsigned int)mdate() );
00928         i = rand() % i_count + 1 ;
00929         /* loop thru the list and count down the unplayed items to the selected one */
00930         for ( i_new = 0; i_new < p_playlist->i_size && i > 0; i_new++ )
00931         {
00932             if ( p_playlist->pp_items[i_new]->i_nb_played == 0 )
00933                 i--;
00934         }
00935         i_new--;
00936 
00937         p_playlist->request.i_skip = 0;
00938         p_playlist->request.b_request = VLC_FALSE;
00939         return p_playlist->pp_items[i_new];
00940     }
00941 
00942     /* Start the real work */
00943     if( p_playlist->request.b_request )
00944     {
00945 #ifdef PLAYLIST_DEBUG
00946         msg_Dbg( p_playlist,"processing request" );
00947 #endif
00948         /* We are not playing from a view */
00949         if( p_playlist->request.i_view == -1  )
00950         {
00951 #ifdef PLAYLIST_DEBUG
00952             msg_Dbg( p_playlist, "non-view mode request");
00953 #endif
00954             /* Directly select the item, just like now */
00955             p_new = p_playlist->request.p_item;
00956             i_skip = p_playlist->request.i_skip;
00957             i_goto = p_playlist->request.i_goto;
00958 
00959             if( p_playlist->i_index < 0 ) p_playlist->i_index = 0;
00960             if ( p_new == NULL )
00961                 p_new = p_playlist->pp_items[p_playlist->i_index];
00962 
00963             if( i_goto >= 0  && i_goto < p_playlist->i_size )
00964             {
00965                 p_playlist->i_index = i_goto;
00966                 p_new = p_playlist->pp_items[p_playlist->i_index];
00967                 p_playlist->request.i_goto = -1;
00968             }
00969 
00970             if( i_skip != 0 )
00971             {
00972                 if( p_playlist->i_index + i_skip < p_playlist->i_size &&
00973                     p_playlist->i_index + i_skip >=  0 )
00974                 {
00975                     p_playlist->i_index += i_skip;
00976                     p_new = p_playlist->pp_items[p_playlist->i_index];
00977                 }
00978                 p_playlist->request.i_skip = 0;
00979             }
00980         }
00981         else
00982         {
00983 #ifdef PLAYLIST_DEBUG
00984             msg_Dbg( p_playlist, "view mode request" );
00985 #endif
00986             p_new = p_playlist->request.p_item;
00987             i_skip = p_playlist->request.i_skip;
00988 
00989             /* If we are asked for a node, take its first item */
00990             if( p_playlist->request.p_item == NULL && i_skip == 0 )
00991             {
00992                 i_skip++;
00993             }
00994 
00995             p_view = playlist_ViewFind( p_playlist,p_playlist->request.i_view );
00996             p_playlist->status.p_node = p_playlist->request.p_node;
00997             p_playlist->status.i_view = p_playlist->request.i_view;
00998             if( !p_view )
00999             {
01000                 msg_Err( p_playlist, "p_view is NULL and should not! (requested view is %i", p_playlist->request.i_view );
01001             }
01002             else if( i_skip > 0 )
01003             {
01004                 for( i = i_skip; i > 0 ; i-- )
01005                 {
01006                     p_new = playlist_FindNextFromParent( p_playlist,
01007                                     p_playlist->request.i_view,
01008                                     p_view->p_root,
01009                                     p_playlist->request.p_node,
01010                                     p_new );
01011                     if( p_new == NULL )
01012                     {
01013 #ifdef PLAYLIST_DEBUG
01014                         msg_Dbg( p_playlist, "looping" );
01015 #endif
01016                         p_new = playlist_FindNextFromParent( p_playlist,
01017                                   p_playlist->request.i_view,
01018                                   p_view->p_root,
01019                                   p_view->p_root,
01020                                   NULL );
01021                         if( p_new == NULL ) break;
01022                     }
01023                 }
01024             }
01025             else if( i_skip < 0 )
01026             {
01027                 for( i = i_skip; i < 0 ; i++ )
01028                 {
01029                     p_new = playlist_FindPrevFromParent( p_playlist,
01030                                     p_playlist->request.i_view,
01031                                     p_view->p_root,
01032                                     p_playlist->request.p_node,
01033                                     p_new );
01034                     if( p_new == NULL )
01035                     {
01036                     /* We reach the beginning of the playlist.
01037                        Go back to the last item. */
01038                         p_new = playlist_RecursiveFindLast( p_playlist,
01039                                                             p_view->p_root );
01040                     }
01041                     if( p_new == NULL ) break;
01042                 }
01043 
01044             }
01045         }
01046         /* Clear the request */
01047         p_playlist->request.b_request = VLC_FALSE;
01048     }
01049     /* "Automatic" item change ( next ) */
01050     else
01051     {
01052         p_playlist->request_date = 0;
01053 
01054         if( p_playlist->status.i_view == -1 )
01055         {
01056 #ifdef PLAYLIST_DEBUG
01057         msg_Dbg( p_playlist, "no request - old mode" );
01058 #endif
01059             if( p_playlist->i_index + 1 < p_playlist->i_size )
01060             {
01061                 p_playlist->i_index++;
01062                 p_new = p_playlist->pp_items[p_playlist->i_index];
01063                 if( !( p_new->i_flags & PLAYLIST_SKIP_FLAG ) )
01064                 {
01065                     return NULL;
01066                 }
01067             }
01068             else
01069             {
01070                 if( b_loop && p_playlist->i_size > 0)
01071                 {
01072                     p_playlist->i_index = 0;
01073                     p_new = p_playlist->pp_items[0];
01074                 }
01075                 else
01076                     p_new = NULL;
01077             }
01078         }
01079         /* We are playing with a view */
01080         else
01081         {
01082 #ifdef PLAYLIST_DEBUG
01083             msg_Dbg( p_playlist,"no request - from a view" );
01084 #endif
01085             playlist_view_t *p_view =
01086                     playlist_ViewFind( p_playlist,
01087                                    p_playlist->status.i_view );
01088             if( !p_view )
01089             {
01090                 msg_Err( p_playlist, "p_view is NULL and should not! (FIXME)" );
01091             }
01092             else
01093             {
01094                 p_new = playlist_FindNextFromParent( p_playlist,
01095                             p_playlist->status.i_view,
01096                             p_view->p_root,
01097                             p_playlist->status.p_node,
01098                             p_playlist->status.p_item );
01099                 if( p_new == NULL && b_loop )
01100                 {
01101 #ifdef PLAYLIST_DEBUG
01102                     msg_Dbg( p_playlist, "looping" );
01103 #endif
01104                     p_new = playlist_FindNextFromParent( p_playlist,
01105                                    p_playlist->status.i_view,
01106                                    p_view->p_root,
01107                                    p_view->p_root,
01108                                    NULL );
01109                 }
01110                 if( p_new != NULL && !(p_new->i_flags & PLAYLIST_SKIP_FLAG) )
01111                     return NULL;
01112             }
01113         }
01114     }
01115 
01116     /* Reset index */
01117     if( p_playlist->i_index >= 0 && p_new != NULL &&
01118             p_playlist->pp_items[p_playlist->i_index] != p_new )
01119     {
01120         p_playlist->i_index = playlist_GetPositionById( p_playlist,
01121                                                         p_new->input.i_id );
01122     }
01123 
01124 #ifdef PLAYLIST_PROFILE
01125     msg_Dbg(p_playlist,"next item found in "I64Fi " us", mdate()-start );
01126 #endif
01127 
01128     if( p_new == NULL )
01129     {
01130         msg_Info( p_playlist, "nothing to play" );
01131     }
01132     return p_new;
01133 }
01134 
01135 /*****************************************************************************
01136  * PlayItem: start the input thread for an item
01137  ****************************************************************************/
01138 static int PlayItem( playlist_t *p_playlist, playlist_item_t *p_item )
01139 {
01140     vlc_value_t val;
01141 
01142     msg_Dbg( p_playlist, "creating new input thread" );
01143 
01144     p_item->i_nb_played++;
01145     p_playlist->status.p_item = p_item;
01146 
01147     p_playlist->i_index = playlist_GetPositionById( p_playlist,
01148                                                     p_item->input.i_id );
01149 
01150 #ifdef PLAYLIST_PROFILE
01151     if( p_playlist->request_date != 0 )
01152     {
01153         msg_Dbg( p_playlist, "request processed after "I64Fi " us",
01154                   mdate() - p_playlist->request_date );
01155     }
01156 #endif
01157 
01158     p_playlist->p_input = input_CreateThread( p_playlist, &p_item->input );
01159 
01160     var_AddCallback( p_playlist->p_input, "item-change",
01161                          ItemChange, p_playlist );
01162 
01163     val.i_int = p_item->input.i_id;
01164     /* unlock the playlist to set the var...mmm */
01165     vlc_mutex_unlock( &p_playlist->object_lock);
01166     var_Set( p_playlist, "playlist-current", val);
01167     vlc_mutex_lock( &p_playlist->object_lock);
01168 
01169     return VLC_SUCCESS;
01170 
01171 }
01172 
01173 /* Forward item change from input */
01174 static int ItemChange( vlc_object_t *p_obj, const char *psz_var,
01175                        vlc_value_t oldval, vlc_value_t newval, void *param )
01176 {
01177     playlist_t *p_playlist = (playlist_t *)param;
01178 
01179     //p_playlist->b_need_update = VLC_TRUE;
01180     var_SetInteger( p_playlist, "item-change", newval.i_int );
01181 
01182     /* Update view */
01183     /* FIXME: Make that automatic */
01184 //    playlist_ViewUpdate( p_playlist, VIEW_S_AUTHOR );
01185 
01186     return VLC_SUCCESS;
01187 }

Generated on Tue Dec 20 10:14:35 2005 for vlc-0.8.4a by  doxygen 1.4.2