Commit defbc4a4 authored by Jakob Leben's avatar Jakob Leben

vlc core: single storage playlist

- Playlist items are stored only once. No category / onelevel separation
- Services Discovery nodes always stay a tree
- The playlist and Media Library are tree or flat depending on variable "playlist-tree".
  It means that if the setting variable says flat, the items that come from playlist
  demuxers are flattened into a single level list.
- The design demanded a different playlist demuxing system. Now playlist demuxers should
  construct a tree of items under an input_item_node_t root and send that using
  input_item_AddSubItemTree. Currently, the old scheme will be retained in modules, because
  there is still some listening to the old event in code (the libvlc media system for example)
parent 53d2132c
......@@ -212,6 +212,7 @@ typedef struct config_category_t config_category_t;
typedef struct input_thread_t input_thread_t;
typedef struct input_thread_sys_t input_thread_sys_t;
typedef struct input_item_t input_item_t;
typedef struct input_item_node_t input_item_node_t;
typedef struct access_t access_t;
typedef struct access_sys_t access_sys_t;
typedef struct stream_t stream_t;
......
......@@ -119,6 +119,7 @@ typedef enum vlc_event_type_t {
/* Input item events */
vlc_InputItemMetaChanged,
vlc_InputItemSubItemAdded,
vlc_InputItemSubItemTreeAdded,
vlc_InputItemDurationChanged,
vlc_InputItemPreparsedChanged,
vlc_InputItemNameChanged,
......@@ -158,6 +159,10 @@ typedef struct vlc_event_t
{
input_item_t * p_new_child;
} input_item_subitem_added;
struct vlc_input_item_subitem_tree_added
{
input_item_node_t * p_root;
} input_item_subitem_tree_added;
struct vlc_input_item_duration_changed
{
mtime_t new_duration;
......
......@@ -107,6 +107,14 @@ enum input_item_type_e
ITEM_TYPE_NUMBER
};
struct input_item_node_t
{
input_item_t * p_item;
int i_children;
input_item_node_t **pp_children;
input_item_node_t *p_parent;
};
VLC_EXPORT( void, input_item_CopyOptions, ( input_item_t *p_parent, input_item_t *p_child ) );
VLC_EXPORT( void, input_item_SetName, ( input_item_t *p_item, const char *psz_name ) );
......@@ -116,6 +124,11 @@ VLC_EXPORT( void, input_item_SetName, ( input_item_t *p_item, const char *psz_na
* the input item children. */
VLC_EXPORT( void, input_item_AddSubItem, ( input_item_t *p_parent, input_item_t *p_child ) );
VLC_EXPORT( void, input_item_AddSubItemTree, ( input_item_node_t *p_root ) );
/* Will send vlc_InputItemSubItemTreeAdded event, just as input_item_AddSubItemTree */
VLC_EXPORT( void, input_item_AddSubItem2, ( input_item_t *p_parent, input_item_t *p_child ) );
/**
* Option flags
......@@ -215,6 +228,14 @@ VLC_EXPORT( input_item_t *, __input_item_NewExt, (vlc_object_t *, const char *ps
*/
#define input_item_New( a,b,c ) input_item_NewExt( a, b, c, 0, NULL, 0, -1 )
VLC_EXPORT( input_item_node_t *, input_item_node_Create, ( input_item_t *p_input ) );
VLC_EXPORT( void, input_item_node_Delete, ( input_item_node_t *p_node ) );
VLC_EXPORT( input_item_node_t *, input_item_node_AppendItem, ( input_item_node_t *p_node, input_item_t *p_item ) );
VLC_EXPORT( void, input_item_node_AppendNode, ( input_item_node_t *p_node, input_item_node_t *p_item ) );
/******************
* Input stats
******************/
......
......@@ -153,7 +153,6 @@ struct playlist_item_t
int i_id; /**< Playlist item specific id */
uint8_t i_flags; /**< Flags */
playlist_t *p_playlist; /**< Parent playlist */
bool b_input_item_observer;
};
#define PLAYLIST_SAVE_FLAG 0x0001 /**< Must it be saved */
......@@ -180,6 +179,11 @@ struct playlist_t
int i_current_index; /**< Index in current array */
/* Predefined items */
playlist_item_t * p_root;
playlist_item_t * p_playing;
playlist_item_t * p_media_library;
//Phony ones, point to those above;
playlist_item_t * p_root_category; /**< Root of category tree */
playlist_item_t * p_root_onelevel; /**< Root of onelevel tree */
playlist_item_t * p_local_category; /** < "Playlist" in CATEGORY view */
......@@ -346,7 +350,6 @@ VLC_EXPORT( int, playlist_DeleteFromInput, ( playlist_t *, input_item_t *, bool
VLC_EXPORT( int, playlist_Add, ( playlist_t *, const char *, const char *, int, int, bool, bool ) );
VLC_EXPORT( int, playlist_AddExt, ( playlist_t *, const char *, const char *, int, int, mtime_t, int, const char *const *, unsigned, bool, bool ) );
VLC_EXPORT( int, playlist_AddInput, ( playlist_t *, input_item_t *, int, int, bool, bool ) );
VLC_EXPORT( int, playlist_BothAddInput, ( playlist_t *, input_item_t *,playlist_item_t *,int , int, int*, int*, bool ) );
/********************************** Item search *************************/
VLC_EXPORT( playlist_item_t *, playlist_ItemGetById, (playlist_t *, int ) );
......
......@@ -60,6 +60,7 @@ static inline void input_item_Init( vlc_object_t *p_o, input_item_t *p_i )
vlc_event_manager_init( p_em, p_i, p_o );
vlc_event_manager_register_event_type( p_em, vlc_InputItemMetaChanged );
vlc_event_manager_register_event_type( p_em, vlc_InputItemSubItemAdded );
vlc_event_manager_register_event_type( p_em, vlc_InputItemSubItemTreeAdded );
vlc_event_manager_register_event_type( p_em, vlc_InputItemDurationChanged );
vlc_event_manager_register_event_type( p_em, vlc_InputItemPreparsedChanged );
vlc_event_manager_register_event_type( p_em, vlc_InputItemNameChanged );
......@@ -268,6 +269,22 @@ void input_item_AddSubItem( input_item_t *p_parent, input_item_t *p_child )
vlc_event_send( &p_parent->event_manager, &event );
}
void input_item_AddSubItemTree ( input_item_node_t *p_root )
{
vlc_event_t event;
event.type = vlc_InputItemSubItemTreeAdded;
event.u.input_item_subitem_tree_added.p_root = p_root;
vlc_event_send( &p_root->p_item->event_manager, &event );
}
void input_item_AddSubItem2 ( input_item_t *p_parent, input_item_t *p_child )
{
input_item_node_t *p_node = input_item_node_Create( p_parent );
input_item_node_AppendItem( p_node, p_child );
input_item_AddSubItemTree( p_node );
input_item_node_Delete( p_node );
}
bool input_item_HasErrorWhenReading( input_item_t *p_item )
{
vlc_mutex_lock( &p_item->lock );
......@@ -962,3 +979,61 @@ static int GuessType( const input_item_t *p_item )
sizeof( tab[0] ), typecmp );
return e ? e->i_type : ITEM_TYPE_FILE;
}
input_item_node_t *input_item_node_Create( input_item_t *p_input )
{
input_item_node_t* p_node = malloc( sizeof( input_item_node_t ) );
if( !p_node )
return NULL;
assert( p_input );
p_node->p_item = p_input;
vlc_gc_incref( p_input );
p_node->p_parent = NULL;
p_node->i_children = 0;
p_node->pp_children = NULL;
return p_node;
}
void input_item_node_Delete( input_item_node_t *p_node )
{
int i;
for( i = 0; i < p_node->i_children; i++ )
input_item_node_Delete( p_node->pp_children[i] );
if( p_node->p_parent )
{
for( i = 0; i < p_node->p_parent->i_children; i++ )
if( p_node->p_parent->pp_children[i] == p_node )
{
REMOVE_ELEM( p_node->p_parent->pp_children,
p_node->p_parent->i_children,
i );
break;
}
}
vlc_gc_decref( p_node->p_item );
free( p_node );
}
input_item_node_t *input_item_node_AppendItem( input_item_node_t *p_node, input_item_t *p_item )
{
input_item_node_t *p_new_child = input_item_node_Create( p_item );
if( !p_new_child ) return NULL;
input_item_node_AppendNode( p_node, p_new_child );
return p_new_child;
}
void input_item_node_AppendNode( input_item_node_t *p_parent, input_item_node_t *p_child )
{
assert( p_parent && p_child && p_child->p_parent == NULL );
INSERT_ELEM( p_parent->pp_children,
p_parent->i_children,
p_parent->i_children,
p_child );
p_child->p_parent = p_parent;
}
......@@ -189,6 +189,8 @@ input_GetItem
input_item_AddInfo
input_item_AddOption
input_item_AddSubItem
input_item_AddSubItem2
input_item_AddSubItemTree
input_item_CopyOptions
input_item_DelInfo
input_item_GetDuration
......@@ -203,6 +205,10 @@ input_item_IsPreparsed
input_item_MetaMatch
__input_item_NewExt
input_item_NewWithType
input_item_node_AppendItem
input_item_node_AppendNode
input_item_node_Create
input_item_node_Delete
input_item_SetDuration
input_item_SetMeta
input_item_SetName
......@@ -313,7 +319,6 @@ playlist_AddExt
playlist_AddInput
playlist_AskForArtEnqueue
playlist_AssertLocked
playlist_BothAddInput
playlist_ChildSearchName
playlist_Clear
playlist_Control
......
......@@ -105,54 +105,50 @@ playlist_t * playlist_Create( vlc_object_t *p_parent )
pl_priv(p_playlist)->b_auto_preparse =
var_InheritBool( p_parent, "auto-preparse" );
PL_LOCK; /* playlist_NodeCreate will check for it */
p_playlist->p_root_category = playlist_NodeCreate( p_playlist, NULL, NULL,
/* Create the root node */
PL_LOCK;
p_playlist->p_root = playlist_NodeCreate( p_playlist, NULL, NULL,
0, NULL );
p_playlist->p_root_onelevel = playlist_NodeCreate( p_playlist, NULL, NULL,
0, p_playlist->p_root_category->p_input );
PL_UNLOCK;
if( !p_playlist->p_root ) return NULL;
if( !p_playlist->p_root_category || !p_playlist->p_root_onelevel )
return NULL;
/* Create currently playing items node */
PL_LOCK;
p_playlist->p_playing = playlist_NodeCreate(
p_playlist, _( "Playlist" ), p_playlist->p_root,
PLAYLIST_RO_FLAG, NULL );
/* Create playlist and media library */
PL_LOCK; /* playlist_NodesPairCreate will check for it */
playlist_NodesPairCreate( p_playlist, _( "Playlist" ),
&p_playlist->p_local_category,
&p_playlist->p_local_onelevel, false );
PL_UNLOCK;
p_playlist->p_local_category->i_flags |= PLAYLIST_RO_FLAG;
p_playlist->p_local_onelevel->i_flags |= PLAYLIST_RO_FLAG;
if( !p_playlist->p_local_category || !p_playlist->p_local_onelevel ||
!p_playlist->p_local_category->p_input ||
!p_playlist->p_local_onelevel->p_input )
return NULL;
if( !p_playlist->p_playing ) return NULL;
/* Create media library node */
const bool b_ml = var_InheritBool( p_parent, "media-library");
if( b_ml )
{
PL_LOCK; /* playlist_NodesPairCreate will check for it */
playlist_NodesPairCreate( p_playlist, _( "Media Library" ),
&p_playlist->p_ml_category,
&p_playlist->p_ml_onelevel, false );
PL_LOCK;
p_playlist->p_media_library = playlist_NodeCreate(
p_playlist, _( "Media Library" ), p_playlist->p_root,
PLAYLIST_RO_FLAG, NULL );
PL_UNLOCK;
if(!p_playlist->p_ml_category || !p_playlist->p_ml_onelevel)
return NULL;
p_playlist->p_ml_category->i_flags |= PLAYLIST_RO_FLAG;
p_playlist->p_ml_onelevel->i_flags |= PLAYLIST_RO_FLAG;
if(!p_playlist->p_media_library ) return NULL;
}
else
{
p_playlist->p_ml_category = p_playlist->p_ml_onelevel = NULL;
p_playlist->p_media_library = NULL;
}
p_playlist->p_root_category = p_playlist->p_root;
p_playlist->p_root_onelevel = p_playlist->p_root;
p_playlist->p_local_category = p_playlist->p_playing;
p_playlist->p_local_onelevel = p_playlist->p_playing;
p_playlist->p_ml_category = p_playlist->p_media_library;
p_playlist->p_ml_onelevel = p_playlist->p_media_library;;
/* Initial status */
pl_priv(p_playlist)->status.p_item = NULL;
pl_priv(p_playlist)->status.p_node = p_playlist->p_local_onelevel;
pl_priv(p_playlist)->status.p_node = p_playlist->p_playing;
pl_priv(p_playlist)->request.b_request = false;
pl_priv(p_playlist)->status.i_status = PLAYLIST_STOPPED;
......
This diff is collapsed.
......@@ -103,16 +103,17 @@ int playlist_Import( playlist_t *p_playlist, const char *psz_file )
/*****************************************************************************
* A subitem has been added to the Media Library (Event Callback)
*****************************************************************************/
static void input_item_subitem_added( const vlc_event_t * p_event,
static void input_item_subitem_tree_added( const vlc_event_t * p_event,
void * user_data )
{
playlist_t *p_playlist = user_data;
input_item_t *p_item = p_event->u.input_item_subitem_added.p_new_child;
input_item_node_t *p_root =
p_event->u.input_item_subitem_tree_added.p_root;
/* playlist_AddInput() can fail, but we have no way to report that ..
* Any way when it has failed, either the playlist is dying, either OOM */
playlist_AddInput( p_playlist, p_item, PLAYLIST_APPEND, PLAYLIST_END,
false, pl_Unlocked );
PL_LOCK;
playlist_InsertInputItemTree ( p_playlist, p_playlist->p_media_library,
p_root, 0, !pl_priv(p_playlist)->b_tree );
PL_UNLOCK;
}
int playlist_MLLoad( playlist_t *p_playlist )
......@@ -156,7 +157,7 @@ int playlist_MLLoad( playlist_t *p_playlist )
return VLC_ENOMEM;
const char *const options[1] = { "meta-file", };
/* that option has to be cleaned in input_item_subitem_added() */
/* that option has to be cleaned in input_item_subitem_tree_added() */
/* vlc_gc_decref() in the same function */
p_input = input_item_NewExt( p_playlist, psz_uri, _("Media Library"),
1, options, VLC_INPUT_OPTION_TRUSTED, -1 );
......@@ -165,19 +166,13 @@ int playlist_MLLoad( playlist_t *p_playlist )
return VLC_EGENERIC;
PL_LOCK;
if( p_playlist->p_ml_onelevel->p_input )
vlc_gc_decref( p_playlist->p_ml_onelevel->p_input );
if( p_playlist->p_ml_category->p_input )
vlc_gc_decref( p_playlist->p_ml_category->p_input );
if( p_playlist->p_media_library->p_input )
vlc_gc_decref( p_playlist->p_media_library->p_input );
p_playlist->p_ml_onelevel->p_input =
p_playlist->p_ml_category->p_input = p_input;
/* We save the input at two different place, incref */
vlc_gc_incref( p_input );
vlc_gc_incref( p_input );
p_playlist->p_media_library->p_input = p_input;
vlc_event_attach( &p_input->event_manager, vlc_InputItemSubItemAdded,
input_item_subitem_added, p_playlist );
vlc_event_attach( &p_input->event_manager, vlc_InputItemSubItemTreeAdded,
input_item_subitem_tree_added, p_playlist );
pl_priv(p_playlist)->b_doing_ml = true;
PL_UNLOCK;
......@@ -190,10 +185,9 @@ int playlist_MLLoad( playlist_t *p_playlist )
pl_priv(p_playlist)->b_doing_ml = false;
PL_UNLOCK;
vlc_event_detach( &p_input->event_manager, vlc_InputItemSubItemAdded,
input_item_subitem_added, p_playlist );
vlc_event_detach( &p_input->event_manager, vlc_InputItemSubItemTreeAdded,
input_item_subitem_tree_added, p_playlist );
vlc_gc_decref( p_input );
return VLC_SUCCESS;
}
......@@ -220,7 +214,7 @@ int playlist_MLDump( playlist_t *p_playlist )
strcat( psz_dirname, DIR_SEP "ml.xspf" );
stats_TimerStart( p_playlist, "ML Dump", STATS_TIMER_ML_DUMP );
playlist_Export( p_playlist, psz_dirname, p_playlist->p_ml_category,
playlist_Export( p_playlist, psz_dirname, p_playlist->p_media_library,
"export-xspf" );
stats_TimerStop( p_playlist, STATS_TIMER_ML_DUMP );
......
......@@ -109,8 +109,7 @@ void pl_Deactivate (libvlc_int_t *);
/* */
playlist_item_t *playlist_ItemNewFromInput( playlist_t *p_playlist,
input_item_t *p_input,
bool install_observer );
input_item_t *p_input );
/* Engine */
playlist_item_t * get_current_status_item( playlist_t * p_playlist);
......@@ -132,6 +131,9 @@ void playlist_SendAddNotify( playlist_t *p_playlist, int i_item_id,
playlist_item_t * playlist_NodeAddInput( playlist_t *, input_item_t *,
playlist_item_t *,int , int, bool );
playlist_item_t *playlist_InsertInputItemTree ( playlist_t *,
playlist_item_t *, input_item_node_t *, int, bool );
/* Tree walking */
playlist_item_t *playlist_ItemFindFromInputAndRoot( playlist_t *p_playlist,
input_item_t *p_input, playlist_item_t *p_root,
......
......@@ -90,8 +90,7 @@ char **vlc_sd_GetNames (vlc_object_t *obj, char ***pppsz_longnames)
struct vlc_sd_internal_t
{
/* the playlist items for category and onelevel */
playlist_item_t *p_cat;
playlist_item_t *p_one;
playlist_item_t *p_node;
services_discovery_t *p_sd; /**< Loaded service discovery modules */
char *psz_name;
};
......@@ -244,8 +243,7 @@ static void playlist_sd_item_added( const vlc_event_t * p_event, void * user_dat
PL_LOCK;
/* If p_parent is in root category (this is clearly a hack) and we have a cat */
if( !EMPTY_STR(psz_cat) &&
p_parent->p_parent == p_playlist->p_root_category )
if( !EMPTY_STR(psz_cat) )
{
/* */
playlist_item_t * p_cat;
......@@ -259,9 +257,9 @@ static void playlist_sd_item_added( const vlc_event_t * p_event, void * user_dat
p_parent = p_cat;
}
playlist_BothAddInput( p_playlist, p_input, p_parent,
PLAYLIST_APPEND, PLAYLIST_END,
NULL, NULL, pl_Locked );
playlist_NodeAddInput( p_playlist, p_input, p_parent,
PLAYLIST_APPEND, PLAYLIST_END,
pl_Locked );
PL_UNLOCK;
}
......@@ -299,22 +297,21 @@ int playlist_ServicesDiscoveryAdd( playlist_t *p_playlist, const char *psz_modul
return VLC_ENOMEM;
}
playlist_item_t * p_cat;
playlist_item_t * p_one;
playlist_item_t *p_node;
PL_LOCK;
playlist_NodesPairCreate( p_playlist, module_get_name( m, true ),
&p_cat, &p_one, false );
p_node = playlist_NodeCreate( p_playlist, module_get_name( m, true ),
p_playlist->p_root, 0, NULL );
PL_UNLOCK;
module_release( m );
vlc_event_attach( services_discovery_EventManager( p_sd ),
vlc_ServicesDiscoveryItemAdded,
playlist_sd_item_added, p_cat );
playlist_sd_item_added, p_node );
vlc_event_attach( services_discovery_EventManager( p_sd ),
vlc_ServicesDiscoveryItemRemoved,
playlist_sd_item_removed, p_cat );
playlist_sd_item_removed, p_node );
if( !vlc_sd_Start( p_sd, psz_module ) )
{
......@@ -324,10 +321,9 @@ int playlist_ServicesDiscoveryAdd( playlist_t *p_playlist, const char *psz_modul
}
/* We want tree-view for service directory */
p_one->p_input->b_prefers_tree = true;
p_node->p_input->b_prefers_tree = true;
p_sds->p_sd = p_sd;
p_sds->p_one = p_one;
p_sds->p_cat = p_cat;
p_sds->p_node = p_node;
p_sds->psz_name = strdup( psz_module );
PL_LOCK;
......@@ -369,21 +365,16 @@ int playlist_ServicesDiscoveryRemove( playlist_t * p_playlist,
vlc_event_detach( services_discovery_EventManager( p_sd ),
vlc_ServicesDiscoveryItemAdded,
playlist_sd_item_added,
p_sds->p_cat );
p_sds->p_node );
vlc_event_detach( services_discovery_EventManager( p_sd ),
vlc_ServicesDiscoveryItemRemoved,
playlist_sd_item_removed,
p_sds->p_cat );
p_sds->p_node );
/* Remove the sd playlist node if it exists */
PL_LOCK;
if( p_sds->p_cat != p_playlist->p_root_category &&
p_sds->p_one != p_playlist->p_root_onelevel )
{
playlist_NodeDelete( p_playlist, p_sds->p_cat, true, false );
playlist_NodeDelete( p_playlist, p_sds->p_one, true, false );
}
playlist_NodeDelete( p_playlist, p_sds->p_node, true, false );
PL_UNLOCK;
vlc_sd_Destroy( p_sd );
......
......@@ -69,7 +69,7 @@ playlist_item_t * playlist_NodeCreate( playlist_t *p_playlist,
p_new_input = input_item_NewWithType( VLC_OBJECT(p_playlist), NULL,
psz_name, 0, NULL, 0, -1, ITEM_TYPE_NODE );
p_item = playlist_ItemNewFromInput( p_playlist,
p_input ? p_input : p_new_input, p_input == NULL );
p_input ? p_input : p_new_input );
if( p_new_input )
vlc_gc_decref( p_new_input );
......@@ -266,69 +266,6 @@ playlist_item_t *playlist_ChildSearchName( playlist_item_t *p_node,
return NULL;
}
/**
* Create a pair of nodes in the category and onelevel trees.
* They share the same input item.
* \param p_playlist the playlist
* \param psz_name the name of the nodes
* \param pp_node_cat pointer to return the node in category tree
* \param pp_node_one pointer to return the node in onelevel tree
* \param b_for_sd For Services Discovery ? (make node read-only and unskipping)
*/
void playlist_NodesPairCreate( playlist_t *p_playlist, const char *psz_name,
playlist_item_t **pp_node_cat,
playlist_item_t **pp_node_one,
bool b_for_sd )
{
PL_ASSERT_LOCKED;
*pp_node_cat = playlist_NodeCreate( p_playlist, psz_name,
p_playlist->p_root_category, 0, NULL );
*pp_node_one = playlist_NodeCreate( p_playlist, psz_name,
p_playlist->p_root_onelevel, 0,
(*pp_node_cat)->p_input );
if( b_for_sd )
{
(*pp_node_cat)->i_flags |= PLAYLIST_RO_FLAG;
(*pp_node_cat)->i_flags |= PLAYLIST_SKIP_FLAG;
(*pp_node_one)->i_flags |= PLAYLIST_RO_FLAG;
(*pp_node_one)->i_flags |= PLAYLIST_SKIP_FLAG;
}
}
/**
* Get the node in the preferred tree from a node in one of category
* or onelevel tree.
*/
playlist_item_t * playlist_GetPreferredNode( playlist_t *p_playlist,
playlist_item_t *p_node )
{
PL_ASSERT_LOCKED;
int i;
if( p_node->p_parent == p_playlist->p_root_category )
{
if( pl_priv(p_playlist)->b_tree || p_node->p_input->b_prefers_tree )
return p_node;
for( i = 0 ; i< p_playlist->p_root_onelevel->i_children; i++ )
{
if( p_playlist->p_root_onelevel->pp_children[i]->p_input ==
p_node->p_input )
return p_playlist->p_root_onelevel->pp_children[i];
}
}
else if( p_node->p_parent == p_playlist->p_root_onelevel )
{
if( !pl_priv(p_playlist)->b_tree || !p_node->p_input->b_prefers_tree )
return p_node;
for( i = 0 ; i< p_playlist->p_root_category->i_children; i++ )
{
if( p_playlist->p_root_category->pp_children[i]->p_input ==
p_node->p_input )
return p_playlist->p_root_category->pp_children[i];
}
}
return NULL;
}
/**********************************************************************
* Tree walking functions
**********************************************************************/
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment