/***************************************************************************** * RunThread: main playlist thread *****************************************************************************/ static void RunThread ( playlist_t *p_playlist ) { vlc_object_t *p_obj; vlc_value_t val; mtime_t i_vout_destroyed_date = 0; mtime_t i_sout_destroyed_date = 0; playlist_item_t *p_autodelete_item = 0; /* Tell above that we're ready */ vlc_thread_ready( p_playlist ); while( !p_playlist->b_die ) { vlc_mutex_lock( &p_playlist->object_lock ); /* If there is an input, check that it doesn't need to die. */ if( p_playlist->p_input ) { /* This input is dead. Remove it ! */ if( p_playlist->p_input->b_dead ) { input_thread_t *p_input; p_input = p_playlist->p_input; p_playlist->p_input = NULL; /* Release the playlist lock, because we may get stuck * in input_DestroyThread() for some time. */ vlc_mutex_unlock( &p_playlist->object_lock ); /* Destroy input */ input_DestroyThread( p_input ); /* Unlink current input * (_after_ input_DestroyThread for vout garbage collector) */ vlc_object_detach( p_input ); /* Destroy object */ vlc_object_destroy( p_input ); i_vout_destroyed_date = 0; i_sout_destroyed_date = 0; /* Check for autodeletion */ if( p_autodelete_item ) { playlist_ItemDelete( p_autodelete_item ); p_autodelete_item = 0; } continue; } /* This input is dying, let him do */ else if( p_playlist->p_input->b_die ) { ; } /* This input has finished, ask him to die ! */ else if( p_playlist->p_input->b_error || p_playlist->p_input->b_eof ) { input_StopThread( p_playlist->p_input ); if( p_playlist->pp_items[p_playlist->i_index]->b_autodeletion ) { /* This ain't pretty but hey it works */ p_autodelete_item = p_playlist->pp_items[p_playlist->i_index]; p_playlist->pp_items[p_playlist->i_index] = playlist_ItemNew( p_playlist, p_autodelete_item->input.psz_uri, 0); vlc_mutex_unlock( &p_playlist->object_lock ); p_playlist->i_status = PLAYLIST_STOPPED; playlist_Delete( p_playlist, p_playlist->i_index ); p_playlist->i_status = PLAYLIST_RUNNING; vlc_mutex_lock( &p_playlist->object_lock ); } SkipItem( p_playlist, 1 ); vlc_mutex_unlock( &p_playlist->object_lock ); continue; } else if( p_playlist->p_input->i_state != INIT_S ) { vlc_mutex_unlock( &p_playlist->object_lock ); i_vout_destroyed_date = ObjectGarbageCollector( p_playlist, VLC_OBJECT_VOUT, i_vout_destroyed_date ); i_sout_destroyed_date = ObjectGarbageCollector( p_playlist, VLC_OBJECT_SOUT, i_sout_destroyed_date ); vlc_mutex_lock( &p_playlist->object_lock ); } } else if( p_playlist->i_status != PLAYLIST_STOPPED ) { /* Start another input. Let's check if that item has * been forced. In that case, we override random (by not skipping) * and play-and-stop */ vlc_bool_t b_forced; var_Get( p_playlist, "prevent-skip", &val ); b_forced = val.b_bool; if( val.b_bool == VLC_FALSE ) { SkipItem( p_playlist, 0 ); } /* Reset forced status */ val.b_bool = VLC_FALSE; var_Set( p_playlist, "prevent-skip", val ); /* Check for play-and-stop */ var_Get( p_playlist, "play-and-stop", &val ); if( val.b_bool == VLC_FALSE || b_forced == VLC_TRUE ) { PlayItem( p_playlist ); } } else if( p_playlist->i_status == PLAYLIST_STOPPED ) { vlc_mutex_unlock( &p_playlist->object_lock ); i_sout_destroyed_date = ObjectGarbageCollector( p_playlist, VLC_OBJECT_SOUT, mdate() ); i_vout_destroyed_date = ObjectGarbageCollector( p_playlist, VLC_OBJECT_VOUT, mdate() ); vlc_mutex_lock( &p_playlist->object_lock ); } vlc_mutex_unlock( &p_playlist->object_lock ); msleep( INTF_IDLE_SLEEP ); } /* If there is an input, kill it */ while( 1 ) { vlc_mutex_lock( &p_playlist->object_lock ); if( p_playlist->p_input == NULL ) { vlc_mutex_unlock( &p_playlist->object_lock ); break; } if( p_playlist->p_input->b_dead ) { input_thread_t *p_input; /* Unlink current input */ p_input = p_playlist->p_input; p_playlist->p_input = NULL; vlc_mutex_unlock( &p_playlist->object_lock ); /* Destroy input */ input_DestroyThread( p_input ); /* Unlink current input (_after_ input_DestroyThread for vout * garbage collector)*/ vlc_object_detach( p_input ); /* Destroy object */ vlc_object_destroy( p_input ); continue; } else if( p_playlist->p_input->b_die ) { /* This input is dying, leave him alone */ ; } else if( p_playlist->p_input->b_error || p_playlist->p_input->b_eof ) { input_StopThread( p_playlist->p_input ); vlc_mutex_unlock( &p_playlist->object_lock ); continue; } else { p_playlist->p_input->b_eof = 1; } vlc_mutex_unlock( &p_playlist->object_lock ); msleep( INTF_IDLE_SLEEP ); } /* close all remaining sout */ while( ( p_obj = vlc_object_find( p_playlist, VLC_OBJECT_SOUT, FIND_CHILD ) ) ) { vlc_object_release( p_obj ); sout_DeleteInstance( (sout_instance_t*)p_obj ); } /* close all remaining vout */ while( ( p_obj = vlc_object_find( p_playlist, VLC_OBJECT_VOUT, FIND_CHILD ) ) ) { vlc_object_detach( p_obj ); vlc_object_release( p_obj ); vout_Destroy( (vout_thread_t *)p_obj ); } }
/** * Do a playlist action. * * If there is something in the playlist then you can do playlist actions. * \param p_playlist the playlist to do the command on * \param i_command the command to do * \param i_arg the argument to the command. See playlist_command_t for details */ void playlist_Command( playlist_t * p_playlist, playlist_command_t i_command, int i_arg ) { vlc_value_t val; vlc_mutex_lock( &p_playlist->object_lock ); if( p_playlist->i_size <= 0 ) { vlc_mutex_unlock( &p_playlist->object_lock ); return; } switch( i_command ) { case PLAYLIST_STOP: p_playlist->i_status = PLAYLIST_STOPPED; if( p_playlist->p_input ) { input_StopThread( p_playlist->p_input ); val.i_int = p_playlist->i_index; /* Does not matter if we unlock here */ vlc_mutex_unlock( &p_playlist->object_lock ); var_Set( p_playlist, "item-change",val ); vlc_mutex_lock( &p_playlist->object_lock ); } break; case PLAYLIST_PLAY: p_playlist->i_status = PLAYLIST_RUNNING; if( !p_playlist->p_input && p_playlist->i_enabled != 0 ) { PlayItem( p_playlist ); } if( p_playlist->p_input ) { val.i_int = PLAYING_S; var_Set( p_playlist->p_input, "state", val ); } break; case PLAYLIST_PAUSE: val.i_int = 0; if( p_playlist->p_input ) var_Get( p_playlist->p_input, "state", &val ); if( val.i_int == PAUSE_S ) { p_playlist->i_status = PLAYLIST_RUNNING; if( p_playlist->p_input ) { val.i_int = PLAYING_S; var_Set( p_playlist->p_input, "state", val ); } } else { p_playlist->i_status = PLAYLIST_PAUSED; if( p_playlist->p_input ) { val.i_int = PAUSE_S; var_Set( p_playlist->p_input, "state", val ); } } break; case PLAYLIST_SKIP: p_playlist->i_status = PLAYLIST_STOPPED; if( p_playlist->i_enabled == 0) { break; } SkipItem( p_playlist, i_arg ); if( p_playlist->p_input ) { input_StopThread( p_playlist->p_input ); } p_playlist->i_status = PLAYLIST_RUNNING; break; case PLAYLIST_GOTO: if( i_arg >= 0 && i_arg < p_playlist->i_size && p_playlist->i_enabled != 0 ) { p_playlist->i_index = i_arg; if( p_playlist->p_input ) { input_StopThread( p_playlist->p_input ); } val.b_bool = VLC_TRUE; var_Set( p_playlist, "prevent-skip", val ); p_playlist->i_status = PLAYLIST_RUNNING; } break; default: msg_Err( p_playlist, "unknown playlist command" ); break; } vlc_mutex_unlock( &p_playlist->object_lock ); #if 0 val.b_bool = VLC_TRUE; var_Set( p_playlist, "intf-change", val ); #endif return; }
/** * Add a playlist item to a given node (in the category view ) * * \param p_playlist the playlist to insert into * \param p_item the playlist item to insert * \param i_view the view for which to add or TODO: ALL_VIEWS * \param p_parent the parent node * \param i_mode the mode used when adding * \param i_pos the possition in the node where to add. If this is * PLAYLIST_END the item will be added at the end of the node ** \return The id of the playlist item */ int playlist_NodeAddItem( playlist_t *p_playlist, playlist_item_t *p_item, int i_view,playlist_item_t *p_parent, int i_mode, int i_pos) { vlc_value_t val; int i_position; playlist_view_t *p_view; playlist_add_t *p_add = (playlist_add_t *)malloc(sizeof( playlist_add_t)); vlc_mutex_lock( &p_playlist->object_lock ); if ( i_pos == PLAYLIST_END ) i_pos = -1; /* Sanity checks */ if( !p_parent || p_parent->i_children == -1 ) { msg_Err( p_playlist, "invalid node" ); } /* * CHECK_INSERT : checks if the item is already enqued before * enqueing it */ if ( i_mode & PLAYLIST_CHECK_INSERT ) { int j; if ( p_playlist->pp_items ) { for ( j = 0; j < p_playlist->i_size; j++ ) { if ( !strcmp( p_playlist->pp_items[j]->input.psz_uri, p_item->input.psz_uri ) ) { playlist_ItemDelete( p_item ); vlc_mutex_unlock( &p_playlist->object_lock ); free( p_add ); return -1; } } } i_mode &= ~PLAYLIST_CHECK_INSERT; i_mode |= PLAYLIST_APPEND; } msg_Dbg( p_playlist, "adding playlist item `%s' ( %s )", p_item->input.psz_name, p_item->input.psz_uri ); p_item->input.i_id = ++p_playlist->i_last_id; /* First, add the item at the right position in the item bank */ /* WHY THAT ? */ //i_position = p_playlist->i_index == -1 ? 0 : p_playlist->i_index; i_position = p_playlist->i_size ; INSERT_ELEM( p_playlist->pp_items, p_playlist->i_size, i_position, p_item ); INSERT_ELEM( p_playlist->pp_all_items, p_playlist->i_all_size, p_playlist->i_all_size, p_item ); p_playlist->i_enabled ++; /* TODO: Handle modes */ playlist_NodeInsert( p_playlist, i_view, p_item, p_parent, i_pos ); p_add->i_item = p_item->input.i_id; p_add->i_node = p_parent->input.i_id; p_add->i_view = i_view; val.p_address = p_add; var_Set( p_playlist, "item-append", val ); /* We update the ALL view directly */ p_view = playlist_ViewFind( p_playlist, VIEW_ALL ); playlist_ItemAddParent( p_item, VIEW_ALL, p_view->p_root ); playlist_ViewUpdate( p_playlist, VIEW_ALL ); /* TODO : Update sorted views*/ if( i_mode & PLAYLIST_GO ) { p_playlist->request.b_request = VLC_TRUE; p_playlist->request.i_view = VIEW_CATEGORY; p_playlist->request.p_node = p_parent; p_playlist->request.p_item = p_item; if( p_playlist->p_input ) { input_StopThread( p_playlist->p_input ); } p_playlist->status.i_status = PLAYLIST_RUNNING; } if( i_mode & PLAYLIST_PREPARSE && var_CreateGetBool( p_playlist, "auto-preparse" ) ) { playlist_PreparseEnqueue( p_playlist, &p_item->input ); } vlc_mutex_unlock( &p_playlist->object_lock ); val.b_bool = VLC_TRUE; // var_Set( p_playlist, "intf-change", val ); // free( p_add ); return p_item->input.i_id; }
/** * Add a playlist item into a playlist * * \param p_playlist the playlist to insert into * \param p_item the playlist item to insert * \param i_mode the mode used when adding * \param i_pos the possition in the playlist where to add. If this is * PLAYLIST_END the item will be added at the end of the playlist * regardless of it's size * \return The id of the playlist item */ int playlist_AddItem( playlist_t *p_playlist, playlist_item_t *p_item, int i_mode, int i_pos) { vlc_value_t val; vlc_mutex_lock( &p_playlist->object_lock ); /* * CHECK_INSERT : checks if the item is already enqued before * enqueing it */ if ( i_mode & PLAYLIST_CHECK_INSERT ) { int j; if ( p_playlist->pp_items ) { for ( j = 0; j < p_playlist->i_size; j++ ) { if ( !strcmp( p_playlist->pp_items[j]->input.psz_uri, p_item->input.psz_uri ) ) { if ( p_item->input.psz_name ) { free( p_item->input.psz_name ); } if ( p_item->input.psz_uri ) { free ( p_item->input.psz_uri ); } free( p_item ); vlc_mutex_unlock( &p_playlist->object_lock ); return -1; } } } i_mode &= ~PLAYLIST_CHECK_INSERT; i_mode |= PLAYLIST_APPEND; } msg_Dbg( p_playlist, "adding playlist item `%s' ( %s )", p_item->input.psz_name, p_item->input.psz_uri ); p_item->i_id = ++p_playlist->i_last_id; /* Do a few boundary checks and allocate space for the item */ if( i_pos == PLAYLIST_END ) { if( i_mode & PLAYLIST_INSERT ) { i_mode &= ~PLAYLIST_INSERT; i_mode |= PLAYLIST_APPEND; } i_pos = p_playlist->i_size - 1; } if( !(i_mode & PLAYLIST_REPLACE) || i_pos < 0 || i_pos >= p_playlist->i_size ) { /* Additional boundary checks */ if( i_mode & PLAYLIST_APPEND ) { i_pos++; } if( i_pos < 0 ) { i_pos = 0; } else if( i_pos > p_playlist->i_size ) { i_pos = p_playlist->i_size; } INSERT_ELEM( p_playlist->pp_items, p_playlist->i_size, i_pos, p_item ); p_playlist->i_enabled ++; if( p_playlist->i_index >= i_pos ) { p_playlist->i_index++; } } else { /* i_mode == PLAYLIST_REPLACE and 0 <= i_pos < p_playlist->i_size */ if( p_playlist->pp_items[i_pos]->input.psz_name ) { free( p_playlist->pp_items[i_pos]->input.psz_name ); } if( p_playlist->pp_items[i_pos]->input.psz_uri ) { free( p_playlist->pp_items[i_pos]->input.psz_uri ); } /* XXX: what if the item is still in use? */ free( p_playlist->pp_items[i_pos] ); p_playlist->pp_items[i_pos] = p_item; } if( i_mode & PLAYLIST_GO ) { p_playlist->i_index = i_pos; if( p_playlist->p_input ) { input_StopThread( p_playlist->p_input ); } p_playlist->i_status = PLAYLIST_RUNNING; } vlc_mutex_unlock( &p_playlist->object_lock ); val.b_bool = VLC_TRUE; var_Set( p_playlist, "intf-change", val ); return p_item->i_id; }
/** * Add a playlist item into a playlist * * \param p_playlist the playlist to insert into * \param p_item the playlist item to insert * \param i_mode the mode used when adding * \param i_pos the possition in the playlist where to add. If this is * PLAYLIST_END the item will be added at the end of the playlist * regardless of it's size * \return The id of the playlist item */ int playlist_AddItem( playlist_t *p_playlist, playlist_item_t *p_item, int i_mode, int i_pos) { vlc_value_t val; vlc_bool_t b_end = VLC_FALSE; playlist_view_t *p_view = NULL; playlist_add_t *p_add = (playlist_add_t *)malloc(sizeof( playlist_add_t)); vlc_mutex_lock( &p_playlist->object_lock ); /* * CHECK_INSERT : checks if the item is already enqued before * enqueing it */ /* That should not change */ if ( i_mode & PLAYLIST_CHECK_INSERT ) { int j; if ( p_playlist->pp_items ) { for ( j = 0; j < p_playlist->i_size; j++ ) { if ( !strcmp( p_playlist->pp_items[j]->input.psz_uri, p_item->input.psz_uri ) ) { playlist_ItemDelete( p_item ); vlc_mutex_unlock( &p_playlist->object_lock ); return -1; } } } i_mode &= ~PLAYLIST_CHECK_INSERT; i_mode |= PLAYLIST_APPEND; } msg_Dbg( p_playlist, "adding playlist item `%s' ( %s )", p_item->input.psz_name, p_item->input.psz_uri ); p_item->input.i_id = ++p_playlist->i_last_id; /* Do a few boundary checks and allocate space for the item */ if( i_pos == PLAYLIST_END ) { b_end = VLC_TRUE; if( i_mode & PLAYLIST_INSERT ) { i_mode &= ~PLAYLIST_INSERT; i_mode |= PLAYLIST_APPEND; } i_pos = p_playlist->i_size - 1; } if( !(i_mode & PLAYLIST_REPLACE) || i_pos < 0 || i_pos >= p_playlist->i_size ) { /* Additional boundary checks */ if( i_mode & PLAYLIST_APPEND ) { i_pos++; } if( i_pos < 0 ) { i_pos = 0; } else if( i_pos > p_playlist->i_size ) { i_pos = p_playlist->i_size; } INSERT_ELEM( p_playlist->pp_items, p_playlist->i_size, i_pos, p_item ); INSERT_ELEM( p_playlist->pp_all_items, p_playlist->i_all_size, p_playlist->i_all_size, p_item ); p_playlist->i_enabled ++; /* We update the ALL view directly */ playlist_ViewUpdate( p_playlist, VIEW_ALL ); /* Add the item to the General category */ if( b_end == VLC_TRUE ) { playlist_NodeAppend( p_playlist, VIEW_CATEGORY, p_item, p_playlist->p_general ); p_add->i_item = p_item->input.i_id; p_add->i_node = p_playlist->p_general->input.i_id; p_add->i_view = VIEW_CATEGORY; val.p_address = p_add; var_Set( p_playlist, "item-append", val ); } else { playlist_NodeInsert( p_playlist, VIEW_CATEGORY, p_item, p_playlist->p_general, i_pos ); } p_view = playlist_ViewFind( p_playlist, VIEW_ALL ); playlist_ItemAddParent( p_item, VIEW_ALL, p_view->p_root ); /* FIXME : Update sorted views */ if( p_playlist->i_index >= i_pos ) { p_playlist->i_index++; } } else { msg_Err( p_playlist, "Insert mode not implemented" ); } if( (i_mode & PLAYLIST_GO ) && p_view ) { p_playlist->request.b_request = VLC_TRUE; /* FIXME ... */ p_playlist->request.i_view = VIEW_CATEGORY; p_playlist->request.p_node = p_view->p_root; p_playlist->request.p_item = p_item; if( p_playlist->p_input ) { input_StopThread( p_playlist->p_input ); } p_playlist->status.i_status = PLAYLIST_RUNNING; } if( i_mode & PLAYLIST_PREPARSE && var_CreateGetBool( p_playlist, "auto-preparse" ) ) { playlist_PreparseEnqueue( p_playlist, &p_item->input ); } vlc_mutex_unlock( &p_playlist->object_lock ); if( b_end == VLC_FALSE ) { val.b_bool = VLC_TRUE; var_Set( p_playlist, "intf-change", val ); } free( p_add ); return p_item->input.i_id; }