thread.c
上传用户:kjfoods
上传日期:2020-07-06
资源大小:29949k
文件大小:19k
源码类别:

midi

开发平台:

Unix_Linux

  1. /*****************************************************************************
  2.  * thread.c : Playlist management functions
  3.  *****************************************************************************
  4.  * Copyright © 1999-2008 the VideoLAN team
  5.  * $Id: eacfd379f37ff5b52fd86a79efa340e543b574de $
  6.  *
  7.  * Authors: Samuel Hocevar <sam@zoy.org>
  8.  *          Clément Stenac <zorglub@videolan.org>
  9.  *
  10.  * This program is free software; you can redistribute it and/or modify
  11.  * it under the terms of the GNU General Public License as published by
  12.  * the Free Software Foundation; either version 2 of the License, or
  13.  * (at your option) any later version.
  14.  *
  15.  * This program is distributed in the hope that it will be useful,
  16.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18.  * GNU General Public License for more details.
  19.  *
  20.  * You should have received a copy of the GNU General Public License
  21.  * along with this program; if not, write to the Free Software
  22.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  23.  *****************************************************************************/
  24. #ifdef HAVE_CONFIG_H
  25. # include "config.h"
  26. #endif
  27. #include <vlc_common.h>
  28. #include <vlc_es.h>
  29. #include <vlc_input.h>
  30. #include <vlc_interface.h>
  31. #include <vlc_playlist.h>
  32. #include "stream_output/stream_output.h"
  33. #include "playlist_internal.h"
  34. /*****************************************************************************
  35.  * Local prototypes
  36.  *****************************************************************************/
  37. static void *Thread   ( void * );
  38. /*****************************************************************************
  39.  * Main functions for the global thread
  40.  *****************************************************************************/
  41. /**
  42.  * Create the main playlist threads.
  43.  * Additionally to the playlist, this thread controls :
  44.  *    - Statistics
  45.  *    - VLM
  46.  * param p_parent
  47.  * return an object with a started thread
  48.  */
  49. void playlist_Activate( playlist_t *p_playlist )
  50. {
  51.     /* */
  52.     playlist_private_t *p_sys = pl_priv(p_playlist);
  53.     /* Fetcher */
  54.     p_sys->p_fetcher = playlist_fetcher_New( p_playlist );
  55.     if( !p_sys->p_fetcher )
  56.         msg_Err( p_playlist, "cannot create playlist fetcher" );
  57.     /* Preparse */
  58.     p_sys->p_preparser = playlist_preparser_New( p_playlist, p_sys->p_fetcher );
  59.     if( !p_sys->p_preparser )
  60.         msg_Err( p_playlist, "cannot create playlist preparser" );
  61.     /* Start the playlist thread */
  62.     if( vlc_clone( &p_sys->thread, Thread, p_playlist,
  63.                    VLC_THREAD_PRIORITY_LOW ) )
  64.     {
  65.         msg_Err( p_playlist, "cannot spawn playlist thread" );
  66.     }
  67.     msg_Dbg( p_playlist, "Activated" );
  68. }
  69. void playlist_Deactivate( playlist_t *p_playlist )
  70. {
  71.     /* */
  72.     playlist_private_t *p_sys = pl_priv(p_playlist);
  73.     msg_Dbg( p_playlist, "Deactivate" );
  74.     vlc_object_kill( p_playlist );
  75.     PL_LOCK;
  76.     vlc_cond_signal( &p_sys->signal );
  77.     PL_UNLOCK;
  78.     vlc_join( p_sys->thread, NULL );
  79.     assert( !p_sys->p_input );
  80.     PL_LOCK;
  81.     playlist_preparser_t *p_preparser = p_sys->p_preparser;
  82.     playlist_fetcher_t *p_fetcher = p_sys->p_fetcher;
  83.     p_sys->p_preparser = NULL;
  84.     p_sys->p_fetcher = NULL;
  85.     PL_UNLOCK;
  86.     if( p_preparser )
  87.         playlist_preparser_Delete( p_preparser );
  88.     if( p_fetcher )
  89.         playlist_fetcher_Delete( p_fetcher );
  90.     /* release input resources */
  91.     if( p_sys->p_input_resource )
  92.         input_resource_Delete( p_sys->p_input_resource );
  93.     p_sys->p_input_resource = NULL;
  94.     /* */
  95.     playlist_MLDump( p_playlist );
  96.     PL_LOCK;
  97.     /* Release the current node */
  98.     set_current_status_node( p_playlist, NULL );
  99.     /* Release the current item */
  100.     set_current_status_item( p_playlist, NULL );
  101.     PL_UNLOCK;
  102.     msg_Dbg( p_playlist, "Deactivated" );
  103. }
  104. /* */
  105. /* Input Callback */
  106. static int InputEvent( vlc_object_t *p_this, char const *psz_cmd,
  107.                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
  108. {
  109.     VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
  110.     playlist_t *p_playlist = p_data;
  111.     if( newval.i_int != INPUT_EVENT_STATE &&
  112.         newval.i_int != INPUT_EVENT_DEAD )
  113.         return VLC_SUCCESS;
  114.     PL_LOCK;
  115.     /* XXX: signaling while not changing any parameter... suspicious... */
  116.     vlc_cond_signal( &pl_priv(p_playlist)->signal );
  117.     PL_UNLOCK;
  118.     return VLC_SUCCESS;
  119. }
  120. static void UpdateActivity( playlist_t *p_playlist, int i_delta )
  121. {
  122.     PL_ASSERT_LOCKED;
  123.     const int i_activity = var_GetInteger( p_playlist, "activity" ) ;
  124.     var_SetInteger( p_playlist, "activity", i_activity + i_delta );
  125. }
  126. /**
  127.  * Synchronise the current index of the playlist
  128.  * to match the index of the current item.
  129.  *
  130.  * param p_playlist the playlist structure
  131.  * param p_cur the current playlist item
  132.  * return nothing
  133.  */
  134. static void ResyncCurrentIndex( playlist_t *p_playlist, playlist_item_t *p_cur )
  135. {
  136.     PL_ASSERT_LOCKED;
  137.     PL_DEBUG( "resyncing on %s", PLI_NAME( p_cur ) );
  138.     /* Simply resync index */
  139.     int i;
  140.     p_playlist->i_current_index = -1;
  141.     for( i = 0 ; i< p_playlist->current.i_size; i++ )
  142.     {
  143.         if( ARRAY_VAL( p_playlist->current, i ) == p_cur )
  144.         {
  145.             p_playlist->i_current_index = i;
  146.             break;
  147.         }
  148.     }
  149.     PL_DEBUG( "%s is at %i", PLI_NAME( p_cur ), p_playlist->i_current_index );
  150. }
  151. static void ResetCurrentlyPlaying( playlist_t *p_playlist,
  152.                                    playlist_item_t *p_cur )
  153. {
  154.     playlist_private_t *p_sys = pl_priv(p_playlist);
  155.     stats_TimerStart( p_playlist, "Items array build",
  156.                       STATS_TIMER_PLAYLIST_BUILD );
  157.     PL_DEBUG( "rebuilding array of current - root %s",
  158.               PLI_NAME( p_sys->status.p_node ) );
  159.     ARRAY_RESET( p_playlist->current );
  160.     p_playlist->i_current_index = -1;
  161.     for( playlist_item_t *p_next = NULL; ; )
  162.     {
  163.         /** FIXME: this is *slow* */
  164.         p_next = playlist_GetNextLeaf( p_playlist,
  165.                                        p_sys->status.p_node,
  166.                                        p_next, true, false );
  167.         if( !p_next )
  168.             break;
  169.         if( p_next == p_cur )
  170.             p_playlist->i_current_index = p_playlist->current.i_size;
  171.         ARRAY_APPEND( p_playlist->current, p_next);
  172.     }
  173.     PL_DEBUG("rebuild done - %i items, index %i", p_playlist->current.i_size,
  174.                                                   p_playlist->i_current_index);
  175.     if( var_GetBool( p_playlist, "random" ) )
  176.     {
  177.         /* Shuffle the array */
  178.         srand( (unsigned int)mdate() );
  179.         for( int j = p_playlist->current.i_size - 1; j > 0; j-- )
  180.         {
  181.             int i = rand() % (j+1); /* between 0 and j */
  182.             playlist_item_t *p_tmp;
  183.             /* swap the two items */
  184.             p_tmp = ARRAY_VAL(p_playlist->current, i);
  185.             ARRAY_VAL(p_playlist->current,i) = ARRAY_VAL(p_playlist->current,j);
  186.             ARRAY_VAL(p_playlist->current,j) = p_tmp;
  187.         }
  188.     }
  189.     p_sys->b_reset_currently_playing = false;
  190.     stats_TimerStop( p_playlist, STATS_TIMER_PLAYLIST_BUILD );
  191. }
  192. /**
  193.  * Start the input for an item
  194.  *
  195.  * param p_playlist the playlist object
  196.  * param p_item the item to play
  197.  * return nothing
  198.  */
  199. static int PlayItem( playlist_t *p_playlist, playlist_item_t *p_item )
  200. {
  201.     playlist_private_t *p_sys = pl_priv(p_playlist);
  202.     input_item_t *p_input = p_item->p_input;
  203.     PL_ASSERT_LOCKED;
  204.     msg_Dbg( p_playlist, "creating new input thread" );
  205.     p_input->i_nb_played++;
  206.     set_current_status_item( p_playlist, p_item );
  207.     p_sys->status.i_status = PLAYLIST_RUNNING;
  208.     UpdateActivity( p_playlist, DEFAULT_INPUT_ACTIVITY );
  209.     assert( p_sys->p_input == NULL );
  210.     input_thread_t *p_input_thread = input_Create( p_playlist, p_input, NULL, p_sys->p_input_resource );
  211.     if( p_input_thread )
  212.     {
  213.         p_sys->p_input = p_input_thread;
  214.         var_AddCallback( p_input_thread, "intf-event", InputEvent, p_playlist );
  215.         if( input_Start( p_sys->p_input ) )
  216.         {
  217.             vlc_object_release( p_input_thread );
  218.             p_sys->p_input = p_input_thread = NULL;
  219.         }
  220.     }
  221.     p_sys->p_input_resource = NULL;
  222.     char *psz_uri = input_item_GetURI( p_item->p_input );
  223.     if( psz_uri && ( !strncmp( psz_uri, "directory:", 10 ) ||
  224.                      !strncmp( psz_uri, "vlc:", 4 ) ) )
  225.     {
  226.         free( psz_uri );
  227.         return VLC_SUCCESS;
  228.     }
  229.     free( psz_uri );
  230.     /* TODO store art policy in playlist private data */
  231.     if( var_GetInteger( p_playlist, "album-art" ) == ALBUM_ART_WHEN_PLAYED )
  232.     {
  233.         bool b_has_art;
  234.         char *psz_arturl, *psz_name;
  235.         psz_arturl = input_item_GetArtURL( p_input );
  236.         psz_name = input_item_GetName( p_input );
  237.         /* p_input->p_meta should not be null after a successfull CreateThread */
  238.         b_has_art = !EMPTY_STR( psz_arturl );
  239.         if( !b_has_art || strncmp( psz_arturl, "attachment://", 13 ) )
  240.         {
  241.             PL_DEBUG( "requesting art for %s", psz_name );
  242.             playlist_AskForArtEnqueue( p_playlist, p_input, pl_Locked );
  243.         }
  244.         free( psz_arturl );
  245.         free( psz_name );
  246.     }
  247.     PL_UNLOCK;
  248.     var_SetInteger( p_playlist, "item-current", p_input->i_id );
  249.     PL_LOCK;
  250.     return VLC_SUCCESS;
  251. }
  252. /**
  253.  * Compute the next playlist item depending on
  254.  * the playlist course mode (forward, backward, random, view,...).
  255.  *
  256.  * param p_playlist the playlist object
  257.  * return nothing
  258.  */
  259. static playlist_item_t *NextItem( playlist_t *p_playlist )
  260. {
  261.     playlist_private_t *p_sys = pl_priv(p_playlist);
  262.     playlist_item_t *p_new = NULL;
  263.     /* Handle quickly a few special cases */
  264.     /* No items to play */
  265.     if( p_playlist->items.i_size == 0 )
  266.     {
  267.         msg_Info( p_playlist, "playlist is empty" );
  268.         return NULL;
  269.     }
  270.     /* Start the real work */
  271.     if( p_sys->request.b_request )
  272.     {
  273.         p_new = p_sys->request.p_item;
  274.         int i_skip = p_sys->request.i_skip;
  275.         PL_DEBUG( "processing request item %s node %s skip %i",
  276.                         PLI_NAME( p_sys->request.p_item ),
  277.                         PLI_NAME( p_sys->request.p_node ), i_skip );
  278.         if( p_sys->request.p_node &&
  279.             p_sys->request.p_node != get_current_status_node( p_playlist ) )
  280.         {
  281.             set_current_status_node( p_playlist, p_sys->request.p_node );
  282.             p_sys->request.p_node = NULL;
  283.             p_sys->b_reset_currently_playing = true;
  284.         }
  285.         /* If we are asked for a node, go to it's first child */
  286.         if( i_skip == 0 && ( p_new == NULL || p_new->i_children != -1 ) )
  287.         {
  288.             i_skip++;
  289.             if( p_new != NULL )
  290.             {
  291.                 p_new = playlist_GetNextLeaf( p_playlist, p_new, NULL, true, false );
  292.                 for( int i = 0; i < p_playlist->current.i_size; i++ )
  293.                 {
  294.                     if( p_new == ARRAY_VAL( p_playlist->current, i ) )
  295.                     {
  296.                         p_playlist->i_current_index = i;
  297.                         i_skip = 0;
  298.                     }
  299.                 }
  300.             }
  301.         }
  302.         if( p_sys->b_reset_currently_playing )
  303.             /* A bit too bad to reset twice ... */
  304.             ResetCurrentlyPlaying( p_playlist, p_new );
  305.         else if( p_new )
  306.             ResyncCurrentIndex( p_playlist, p_new );
  307.         else
  308.             p_playlist->i_current_index = -1;
  309.         if( p_playlist->current.i_size && (i_skip > 0) )
  310.         {
  311.             if( p_playlist->i_current_index < -1 )
  312.                 p_playlist->i_current_index = -1;
  313.             for( int i = i_skip; i > 0 ; i-- )
  314.             {
  315.                 p_playlist->i_current_index++;
  316.                 if( p_playlist->i_current_index >= p_playlist->current.i_size )
  317.                 {
  318.                     PL_DEBUG( "looping - restarting at beginning of node" );
  319.                     p_playlist->i_current_index = 0;
  320.                 }
  321.             }
  322.             p_new = ARRAY_VAL( p_playlist->current,
  323.                                p_playlist->i_current_index );
  324.         }
  325.         else if( p_playlist->current.i_size && (i_skip < 0) )
  326.         {
  327.             for( int i = i_skip; i < 0 ; i++ )
  328.             {
  329.                 p_playlist->i_current_index--;
  330.                 if( p_playlist->i_current_index <= -1 )
  331.                 {
  332.                     PL_DEBUG( "looping - restarting at end of node" );
  333.                     p_playlist->i_current_index = p_playlist->current.i_size-1;
  334.                 }
  335.             }
  336.             p_new = ARRAY_VAL( p_playlist->current,
  337.                                p_playlist->i_current_index );
  338.         }
  339.         /* Clear the request */
  340.         p_sys->request.b_request = false;
  341.     }
  342.     /* "Automatic" item change ( next ) */
  343.     else
  344.     {
  345.         bool b_loop = var_GetBool( p_playlist, "loop" );
  346.         bool b_repeat = var_GetBool( p_playlist, "repeat" );
  347.         bool b_playstop = var_GetBool( p_playlist, "play-and-stop" );
  348.         /* Repeat and play/stop */
  349.         if( b_repeat && get_current_status_item( p_playlist ) )
  350.         {
  351.             msg_Dbg( p_playlist,"repeating item" );
  352.             return get_current_status_item( p_playlist );
  353.         }
  354.         if( b_playstop )
  355.         {
  356.             msg_Dbg( p_playlist,"stopping (play and stop)" );
  357.             return NULL;
  358.         }
  359.         /* */
  360.         if( get_current_status_item( p_playlist ) )
  361.         {
  362.             playlist_item_t *p_parent = get_current_status_item( p_playlist );
  363.             while( p_parent )
  364.             {
  365.                 if( p_parent->i_flags & PLAYLIST_SKIP_FLAG )
  366.                 {
  367.                     msg_Dbg( p_playlist, "blocking item, stopping") ;
  368.                     return NULL;
  369.                 }
  370.                 p_parent = p_parent->p_parent;
  371.             }
  372.         }
  373.         PL_DEBUG( "changing item without a request (current %i/%i)",
  374.                   p_playlist->i_current_index, p_playlist->current.i_size );
  375.         /* Cant go to next from current item */
  376.         if( get_current_status_item( p_playlist ) &&
  377.             get_current_status_item( p_playlist )->i_flags & PLAYLIST_SKIP_FLAG )
  378.             return NULL;
  379.         if( p_sys->b_reset_currently_playing )
  380.             ResetCurrentlyPlaying( p_playlist,
  381.                                    get_current_status_item( p_playlist ) );
  382.         p_playlist->i_current_index++;
  383.         assert( p_playlist->i_current_index <= p_playlist->current.i_size );
  384.         if( p_playlist->i_current_index == p_playlist->current.i_size )
  385.         {
  386.             if( !b_loop || p_playlist->current.i_size == 0 )
  387.                 return NULL;
  388.             p_playlist->i_current_index = 0;
  389.         }
  390.         PL_DEBUG( "using item %i", p_playlist->i_current_index );
  391.         if ( p_playlist->current.i_size == 0 )
  392.             return NULL;
  393.         p_new = ARRAY_VAL( p_playlist->current, p_playlist->i_current_index );
  394.         /* The new item can't be autoselected  */
  395.         if( p_new != NULL && p_new->i_flags & PLAYLIST_SKIP_FLAG )
  396.             return NULL;
  397.     }
  398.     return p_new;
  399. }
  400. static int LoopInput( playlist_t *p_playlist )
  401. {
  402.     playlist_private_t *p_sys = pl_priv(p_playlist);
  403.     input_thread_t *p_input = p_sys->p_input;
  404.     if( !p_input )
  405.         return VLC_EGENERIC;
  406.     if( ( p_sys->request.b_request || !vlc_object_alive( p_playlist ) ) && !p_input->b_die )
  407.     {
  408.         PL_DEBUG( "incoming request - stopping current input" );
  409.         input_Stop( p_input, true );
  410.     }
  411.     /* This input is dead. Remove it ! */
  412.     if( p_input->b_dead )
  413.     {
  414.         PL_DEBUG( "dead input" );
  415.         assert( p_sys->p_input_resource == NULL );
  416.         p_sys->p_input_resource = input_DetachResource( p_input );
  417.         PL_UNLOCK;
  418.         /* We can unlock as we return VLC_EGENERIC (no event will be lost) */
  419.         /* input_resource_t must be manipulated without playlist lock */
  420.         if( !var_CreateGetBool( p_input, "sout-keep" ) )
  421.             input_resource_TerminateSout( p_sys->p_input_resource );
  422.         /* The DelCallback must be issued without playlist lock */
  423.         var_DelCallback( p_input, "intf-event", InputEvent, p_playlist );
  424.         PL_LOCK;
  425.         p_sys->p_input = NULL;
  426.         vlc_thread_join( p_input );
  427.         vlc_object_release( p_input );
  428.         UpdateActivity( p_playlist, -DEFAULT_INPUT_ACTIVITY );
  429.         return VLC_EGENERIC;
  430.     }
  431.     /* This input is dying, let it do */
  432.     else if( p_input->b_die )
  433.     {
  434.         PL_DEBUG( "dying input" );
  435.     }
  436.     /* This input has finished, ask it to die ! */
  437.     else if( p_input->b_error || p_input->b_eof )
  438.     {
  439.         PL_DEBUG( "finished input" );
  440.         input_Stop( p_input, false );
  441.     }
  442.     return VLC_SUCCESS;
  443. }
  444. static void LoopRequest( playlist_t *p_playlist )
  445. {
  446.     playlist_private_t *p_sys = pl_priv(p_playlist);
  447.     assert( !p_sys->p_input );
  448.     /* No input. Several cases
  449.      *  - No request, running status -> start new item
  450.      *  - No request, stopped status -> collect garbage
  451.      *  - Request, running requested -> start new item
  452.      *  - Request, stopped requested -> collect garbage
  453.     */
  454.     const int i_status = p_sys->request.b_request ?
  455.                          p_sys->request.i_status : p_sys->status.i_status;
  456.     if( i_status == PLAYLIST_STOPPED || !vlc_object_alive( p_playlist ) )
  457.     {
  458.         p_sys->status.i_status = PLAYLIST_STOPPED;
  459.         if( p_sys->p_input_resource &&
  460.             input_resource_HasVout( p_sys->p_input_resource ) )
  461.         {
  462.             /* XXX We can unlock if we don't issue the wait as we will be
  463.              * call again without anything else done between the calls */
  464.             PL_UNLOCK;
  465.             /* input_resource_t must be manipulated without playlist lock */
  466.             input_resource_TerminateVout( p_sys->p_input_resource );
  467.             PL_LOCK;
  468.         }
  469.         else
  470.         {
  471.             if( vlc_object_alive( p_playlist ) )
  472.                 vlc_cond_wait( &p_sys->signal, &p_sys->lock );
  473.         }
  474.         return;
  475.     }
  476.     playlist_item_t *p_item = NextItem( p_playlist );
  477.     if( p_item )
  478.     {
  479.         msg_Dbg( p_playlist, "starting new item" );
  480.         PlayItem( p_playlist, p_item );
  481.         return;
  482.     }
  483.     msg_Dbg( p_playlist, "nothing to play" );
  484.     p_sys->status.i_status = PLAYLIST_STOPPED;
  485.     if( var_GetBool( p_playlist, "play-and-exit" ) )
  486.     {
  487.         msg_Info( p_playlist, "end of playlist, exiting" );
  488.         libvlc_Quit( p_playlist->p_libvlc );
  489.     }
  490. }
  491. /**
  492.  * Run the main control thread itself
  493.  */
  494. static void *Thread ( void *data )
  495. {
  496.     playlist_t *p_playlist = data;
  497.     playlist_private_t *p_sys = pl_priv(p_playlist);
  498.     playlist_Lock( p_playlist );
  499.     while( vlc_object_alive( p_playlist ) || p_sys->p_input )
  500.     {
  501.         /* FIXME: what's that ! */
  502.         if( p_sys->b_reset_currently_playing &&
  503.             mdate() - p_sys->last_rebuild_date > 30000 ) // 30 ms
  504.         {
  505.             ResetCurrentlyPlaying( p_playlist,
  506.                                    get_current_status_item( p_playlist ) );
  507.             p_sys->last_rebuild_date = mdate();
  508.         }
  509.         /* If there is an input, check that it doesn't need to die. */
  510.         while( !LoopInput( p_playlist ) )
  511.             vlc_cond_wait( &p_sys->signal, &p_sys->lock );
  512.         LoopRequest( p_playlist );
  513.     }
  514.     playlist_Unlock( p_playlist );
  515.     return NULL;
  516. }