[vlc-devel] [PATCH 02/12] access: refactor pf_readdir

Thomas Guillem thomas at gllm.fr
Tue May 19 10:27:42 CEST 2015


The main advantage is to move the management of the input_item_node_t from all
accesses to the directory demux.
---
 include/vlc_access.h                |   6 +-
 include/vlc_stream.h                |   6 +-
 modules/access/archive/stream.c     |  19 ++----
 modules/access/directory.c          |  28 ++------
 modules/access/dsm/access.c         | 131 +++++++++++++++++-------------------
 modules/access/fs.h                 |   2 +-
 modules/access/ftp.c                |  17 ++---
 modules/access/sftp.c               |  28 ++++----
 modules/demux/playlist/directory.c  |  30 +++++++--
 modules/services_discovery/upnp.cpp | 109 ++++++++++++++++++------------
 modules/services_discovery/upnp.hpp |  16 +++--
 src/input/stream.c                  |  15 +++--
 src/input/stream_filter.c           |   4 +-
 13 files changed, 212 insertions(+), 199 deletions(-)

diff --git a/include/vlc_access.h b/include/vlc_access.h
index 1f3cb03..a23d3b1 100644
--- a/include/vlc_access.h
+++ b/include/vlc_access.h
@@ -92,7 +92,11 @@ struct access_t
      * XXX A access should set one and only one of them */
     ssize_t     (*pf_read)   ( access_t *, uint8_t *, size_t );  /* Return -1 if no data yet, 0 if no more data, else real data read */
     block_t    *(*pf_block)  ( access_t * );                     /* Return a block of data in his 'natural' size, NULL if not yet data or eof */
-    int         (*pf_readdir)( access_t *, input_item_node_t * );/* Fills the provided item_node, see doc/browsing.txt for details */
+
+    /* pf_readdir: Read the next input_item_t from the directory stream. It
+     * returns the next input item on success or NULL in case of error or end
+     * of stream. The item must be released with input_item_Release. */
+    input_item_t *(*pf_readdir)( access_t * );
 
     /* Called for each seek.
      * XXX can be null */
diff --git a/include/vlc_stream.h b/include/vlc_stream.h
index 37e6b2d..a9e8586 100644
--- a/include/vlc_stream.h
+++ b/include/vlc_stream.h
@@ -67,7 +67,7 @@ struct stream_t
     /* */
     int         (*pf_read)   ( stream_t *, void *p_read, unsigned int i_read );
     int         (*pf_peek)   ( stream_t *, const uint8_t **pp_peek, unsigned int i_peek );
-    int         (*pf_readdir)( stream_t *, input_item_node_t * );
+    input_item_t *(*pf_readdir)( stream_t * );
     int         (*pf_control)( stream_t *, int i_query, va_list );
 
     /* */
@@ -134,7 +134,7 @@ VLC_API int stream_Control( stream_t *s, int i_query, ... );
 VLC_API block_t * stream_Block( stream_t *s, int i_size );
 VLC_API block_t * stream_BlockRemaining( stream_t *s, int i_max_size );
 VLC_API char * stream_ReadLine( stream_t * );
-VLC_API int stream_ReadDir( stream_t *, input_item_node_t * );
+VLC_API input_item_t *stream_ReadDir( stream_t * );
 
 /**
  * Get the current position in a stream
@@ -230,7 +230,7 @@ VLC_API stream_t* stream_FilterNew( stream_t *p_source, const char *psz_stream_f
  * Default ReadDir implementation for stream Filter. This implementation just
  * forward the pf_readdir call to the p_source stream.
  */
-VLC_API int stream_FilterDefaultReadDir( stream_t *s, input_item_node_t *p_node );
+VLC_API input_item_t *stream_FilterDefaultReadDir( stream_t *s );
 
 /**
  * Sets stream_FilterDefaultReadDir as the pf_readdir callback for this stream filter
diff --git a/modules/access/archive/stream.c b/modules/access/archive/stream.c
index 7877b7b..a74bf53 100644
--- a/modules/access/archive/stream.c
+++ b/modules/access/archive/stream.c
@@ -132,39 +132,34 @@ static ssize_t SeekCallback(struct archive *p_archive, void *p_object, ssize_t i
     return stream_Tell(p_stream->p_source);
 }
 
-static int Browse(stream_t *p_stream, input_item_node_t *p_node)
+static input_item_t *Browse(stream_t *p_stream)
 {
     stream_sys_t *p_sys = p_stream->p_sys;
     struct archive_entry *p_entry;
+    input_item_t *p_item = NULL;
 
-    while(archive_read_next_header(p_sys->p_archive, &p_entry) == ARCHIVE_OK)
+    if (archive_read_next_header(p_sys->p_archive, &p_entry) == ARCHIVE_OK)
     {
         char *psz_uri = NULL;
         char *psz_access_uri = NULL;
         int i_ret = asprintf(&psz_access_uri, "%s://%s%c%s", p_stream->psz_access,
                              p_stream->psz_path, ARCHIVE_SEP_CHAR, archive_entry_pathname(p_entry));
         if (i_ret == -1)
-            goto error;
+            return NULL;
         i_ret = asprintf(&psz_uri, "archive://%s", psz_access_uri);
         free(psz_access_uri);
         if( i_ret == -1 )
-            goto error;
+            return NULL;
 
         input_item_t *p_item = input_item_New(psz_uri, archive_entry_pathname(p_entry));
         free( psz_uri );
         if(p_item == NULL)
-            goto error;
+            return NULL;
 
-        input_item_CopyOptions(p_node->p_item, p_item);
-        input_item_node_AppendItem(p_node, p_item);
         msg_Dbg(p_stream, "declaring playlist entry %s", archive_entry_pathname(p_entry));
-        input_item_Release(p_item);
     }
 
-    return VLC_SUCCESS;
-
-error:
-    return VLC_ENOITEM;
+    return p_item;
 }
 
 int StreamOpen(vlc_object_t *p_object)
diff --git a/modules/access/directory.c b/modules/access/directory.c
index 5cac748..270e04f 100644
--- a/modules/access/directory.c
+++ b/modules/access/directory.c
@@ -353,27 +353,19 @@ void DirClose( vlc_object_t * p_this )
 /* This function is a little bit too complex for what it seems to do, but the
  * point is to de-recursify directory recusion to avoid overruning the stack
  * in case there's a high directory depth */
-int DirRead (access_t *p_access, input_item_node_t *p_current_node)
+input_item_t* DirRead (access_t *p_access)
 {
     access_sys_t *p_sys = p_access->p_sys;
+    input_item_t *p_item = NULL;
 
-    while (p_sys->current != NULL
+    while (!p_item && p_sys->current != NULL
            && p_sys->current->i <= p_sys->current->filec)
     {
         directory *p_current = p_sys->current;
 
-        /* End of the current folder, let's pop directory and node */
-        if (p_current->i == p_current->filec)
-        {
-            directory_pop (p_sys);
-            p_current_node = p_current_node->p_parent;
-            continue;
-        }
-
         char *psz_entry = p_current->filev[p_current->i++];
         char *psz_full_uri, *psz_uri;
         DIR *handle;
-        input_item_t *p_new = NULL;
         int i_res;
 
         /* Check if it is a directory or even readable */
@@ -383,7 +375,6 @@ int DirRead (access_t *p_access, input_item_node_t *p_current_node)
             || (i_res == ENTRY_ENOTDIR && has_ext (p_sys->ignored_exts, psz_entry)))
             continue;
 
-
         /* Create an input item for the current entry */
         psz_uri = encode_URI_component (psz_entry);
         if (psz_uri == NULL
@@ -398,21 +389,16 @@ int DirRead (access_t *p_access, input_item_node_t *p_current_node)
         }
 
         int i_type = i_res == ENTRY_DIR ? ITEM_TYPE_DIRECTORY : ITEM_TYPE_FILE;
-        p_new = input_item_NewWithType (psz_full_uri, psz_entry,
-                                        0, NULL, 0, 0, i_type);
-        if (p_new == NULL)
+        p_item = input_item_NewWithType (psz_full_uri, psz_entry,
+                                         0, NULL, 0, 0, i_type);
+        if (p_item == NULL)
         {
             free (psz_full_uri);
             closedir (handle);
             continue;
         }
 
-        input_item_CopyOptions (p_current_node->p_item, p_new);
-        input_item_node_AppendItem (p_current_node, p_new);
-
         free (psz_full_uri);
-        input_item_Release (p_new);
     }
-
-    return VLC_SUCCESS;
+    return p_item;
 }
diff --git a/modules/access/dsm/access.c b/modules/access/dsm/access.c
index 5695718..ecf597a 100644
--- a/modules/access/dsm/access.c
+++ b/modules/access/dsm/access.c
@@ -107,8 +107,7 @@ static void login_dialog( access_t *p_access );
 static int login( access_t *p_access );
 static void backslash_path( vlc_url_t *p_url );
 static bool get_path( access_t *p_access );
-static int add_item( access_t *p_access,  input_item_node_t *p_node,
-                     const char *psz_name, int i_type );
+static input_item_t* new_item( access_t *p_access, const char *psz_name, int i_type );
 
 struct access_sys_t
 {
@@ -126,6 +125,11 @@ struct access_sys_t
     smb_fd              i_fd;               /**< SMB fd for the file we're reading */
     smb_tid             i_tid;              /**< SMB Tree ID we're connected to */
     bool                b_is_browsing;
+
+    size_t              i_browse_count;
+    size_t              i_browse_idx;
+    smb_share_list      shares;
+    smb_stat_list       files;
 };
 
 /*****************************************************************************
@@ -240,6 +244,10 @@ static void Close( vlc_object_t *p_this )
     if( p_sys->p_session )
         smb_session_destroy( p_sys->p_session );
     vlc_UrlClean( &p_sys->url );
+    if( p_sys->shares )
+        smb_share_list_destroy( p_sys->shares );
+    if( p_sys->files )
+        smb_stat_list_destroy( p_sys->files );
     free( p_sys->creds.login );
     free( p_sys->creds.password );
     free( p_sys->creds.domain );
@@ -559,137 +567,120 @@ static int Control( access_t *p_access, int i_query, va_list args )
     return VLC_SUCCESS;
 }
 
-static int add_item( access_t *p_access, input_item_node_t *p_node,
-                     const char *psz_name, int i_type )
+static input_item_t *new_item( access_t *p_access, const char *psz_name,
+                               int i_type )
 {
     access_sys_t *p_sys = p_access->p_sys;
     input_item_t *p_item;
-    char         *psz_uri, *psz_option;
+    char         *psz_uri, *psz_option = NULL;
     int           i_ret;
 
-    i_ret = asprintf( &psz_uri, "%s/%s", p_node->p_item->psz_uri, psz_name );
+    i_ret = asprintf( &psz_uri, "smb://%s/%s", p_access->psz_location, psz_name );
     if( i_ret == -1 )
-        return VLC_ENOMEM;
+        return NULL;
 
     p_item = input_item_NewWithTypeExt( psz_uri, psz_name, 0, NULL, 0, -1,
                                         i_type, 1 );
     free( psz_uri );
     if( p_item == NULL )
-        return VLC_ENOMEM;
+        return NULL;
 
     /* Here we save on the node the credentials that allowed us to login.
      * That way the user isn't prompted more than once for credentials */
     i_ret = asprintf( &psz_option, "smb-user=%s", p_sys->creds.login );
     if( i_ret == -1 )
-        return VLC_ENOMEM;
+        goto bailout;
     input_item_AddOption( p_item, psz_option, VLC_INPUT_OPTION_TRUSTED );
     free( psz_option );
 
     i_ret = asprintf( &psz_option, "smb-pwd=%s", p_sys->creds.password );
     if( i_ret == -1 )
-        return VLC_ENOMEM;
+        goto bailout;
     input_item_AddOption( p_item, psz_option, VLC_INPUT_OPTION_TRUSTED );
     free( psz_option );
 
     i_ret = asprintf( &psz_option, "smb-domain=%s", p_sys->creds.domain );
     if( i_ret == -1 )
-        return VLC_ENOMEM;
+        goto bailout;
     input_item_AddOption( p_item, psz_option, VLC_INPUT_OPTION_TRUSTED );
     free( psz_option );
 
-    input_item_CopyOptions( p_node->p_item, p_item );
-    i_ret = input_item_node_AppendItem( p_node, p_item ) != NULL ? VLC_SUCCESS
-                                                                 : VLC_EGENERIC;
-
-    input_item_Release( p_item );
-    return i_ret;
+    return p_item;
+bailout:
+    if( p_item )
+        input_item_Release( p_item );
+    free( psz_option );
+    return NULL;
 }
 
-static int BrowseShare( access_t *p_access, input_item_node_t *p_node )
+static input_item_t* BrowseShare( access_t *p_access )
 {
     access_sys_t *p_sys = p_access->p_sys;
-    smb_share_list  shares;
     const char     *psz_name;
-    size_t          share_count;
-    int             i_ret;
+    input_item_t   *p_item = NULL;
 
-    share_count = smb_share_get_list( p_sys->p_session, &shares );
-    if( !share_count )
-        return VLC_ENOITEM;
-
-    for( size_t i = 0; i < share_count; i++ )
+    if( !p_sys->i_browse_count )
+        p_sys->i_browse_count = smb_share_get_list( p_sys->p_session,
+                                                    &p_sys->shares );
+    for( ; !p_item && p_sys->i_browse_idx < p_sys->i_browse_count
+         ; p_sys->i_browse_idx++ )
     {
-        psz_name = smb_share_list_at( shares, i );
+        psz_name = smb_share_list_at( p_sys->shares, p_sys->i_browse_idx );
 
         if( psz_name[strlen( psz_name ) - 1] == '$')
             continue;
 
-        i_ret = add_item( p_access, p_node, psz_name, ITEM_TYPE_DIRECTORY );
-        if( i_ret != VLC_SUCCESS )
-            goto error;
+        p_item = new_item( p_access, psz_name, ITEM_TYPE_DIRECTORY );
+        if( !p_item )
+            return NULL;
     }
-
-    smb_share_list_destroy( shares );
-    return VLC_SUCCESS;
-    error:
-        smb_share_list_destroy( shares );
-        return i_ret;
+    return p_item;
 }
 
-static int BrowseDirectory( access_t *p_access, input_item_node_t *p_node )
+static input_item_t* BrowseDirectory( access_t *p_access )
 {
     access_sys_t *p_sys = p_access->p_sys;
-    smb_stat_list   files;
     smb_stat        st;
+    input_item_t   *p_item = NULL;
     char           *psz_query;
     const char     *psz_name;
-    size_t          files_count;
     int             i_ret;
 
-    if( p_sys->psz_path != NULL )
+    if( !p_sys->i_browse_count )
     {
-        i_ret = asprintf( &psz_query, "%s\\*", p_sys->psz_path );
-        if( i_ret == -1 )
-            return VLC_ENOMEM;
-        files = smb_find( p_sys->p_session, p_sys->i_tid, psz_query );
-        free( psz_query );
+        if( p_sys->psz_path != NULL )
+        {
+            i_ret = asprintf( &psz_query, "%s\\*", p_sys->psz_path );
+            if( i_ret == -1 )
+                return NULL;
+            p_sys->files = smb_find( p_sys->p_session, p_sys->i_tid, psz_query );
+            free( psz_query );
+        }
+        else
+            p_sys->files = smb_find( p_sys->p_session, p_sys->i_tid, "\\*" );
+        if( p_sys->files == NULL )
+            return NULL;
+        p_sys->i_browse_count = smb_stat_list_count( p_sys->files );
     }
-    else
-        files = smb_find( p_sys->p_session, p_sys->i_tid, "\\*" );
-
-    if( files == NULL )
-        return VLC_ENOITEM;
 
-    files_count = smb_stat_list_count( files );
-    for( size_t i = 0; i < files_count; i++ )
+    if( p_sys->i_browse_idx < p_sys->i_browse_count )
     {
         int i_type;
 
-        st = smb_stat_list_at( files, i );
+        st = smb_stat_list_at( p_sys->files, p_sys->i_browse_idx++ );
 
-        if( st == NULL ) {
-            i_ret = VLC_ENOITEM;
-            goto error;
-        }
+        if( st == NULL )
+            return NULL;
 
         psz_name = smb_stat_name( st );
 
-        /* Avoid infinite loop */
-        if( !strcmp( psz_name, ".") || !strcmp( psz_name, "..") )
-            continue;
         i_type = smb_stat_get( st, SMB_STAT_ISDIR ) ?
                  ITEM_TYPE_DIRECTORY : ITEM_TYPE_FILE;
-        i_ret = add_item( p_access, p_node, psz_name, i_type );
-        if( i_ret != VLC_SUCCESS )
-            goto error;
+        p_item = new_item( p_access, psz_name, i_type );
+        if( !p_item )
+            return NULL;
     }
-
-    smb_stat_list_destroy( files );
-    return VLC_SUCCESS;
-
-    error:
-        smb_stat_list_destroy( files );
-        return i_ret;
+    return p_item;
 }
 
 static int BrowserInit( access_t *p_access )
diff --git a/modules/access/fs.h b/modules/access/fs.h
index 3bcf048..e948ba2 100644
--- a/modules/access/fs.h
+++ b/modules/access/fs.h
@@ -25,6 +25,6 @@ void FileClose (vlc_object_t *);
 
 int DirOpen (vlc_object_t *);
 int DirInit (access_t *p_access, DIR *handle);
-int DirRead (access_t *, input_item_node_t *);
+input_item_t* DirRead (access_t *);
 int DirControl (access_t *, int, va_list);
 void DirClose (vlc_object_t *);
diff --git a/modules/access/ftp.c b/modules/access/ftp.c
index cf80c14..e9837aa 100644
--- a/modules/access/ftp.c
+++ b/modules/access/ftp.c
@@ -106,7 +106,7 @@ vlc_module_end ()
 static ssize_t Read( access_t *, uint8_t *, size_t );
 static int Seek( access_t *, uint64_t );
 static int Control( access_t *, int, va_list );
-static int DirRead( access_t *, input_item_node_t * );
+static input_item_t* DirRead( access_t * );
 #ifdef ENABLE_SOUT
 static int OutSeek( sout_access_out_t *, off_t );
 static ssize_t Write( sout_access_out_t *, block_t * );
@@ -839,9 +839,10 @@ static ssize_t Read( access_t *p_access, uint8_t *p_buffer, size_t i_len )
 /*****************************************************************************
  * DirRead:
  *****************************************************************************/
-static int DirRead (access_t *p_access, input_item_node_t *p_current_node)
+static input_item_t* DirRead( access_t *p_access )
 {
     access_sys_t *p_sys = p_access->p_sys;
+    input_item_t *p_item = NULL;
 
     assert( p_sys->data.fd != -1 );
     assert( !p_sys->out );
@@ -863,23 +864,13 @@ static int DirRead (access_t *p_access, input_item_node_t *p_current_node)
                       p_sys->url.psz_path ? p_sys->url.psz_path : "",
                       psz_line ) != -1 )
         {
-            input_item_t *p_item;
-
             p_item = input_item_NewWithTypeExt( psz_uri, psz_line, 0, NULL,
                                                 0, -1, ITEM_TYPE_UNKNOWN, 1 );
             free( psz_uri );
-            if( !p_item )
-            {
-                free( psz_line );
-                return VLC_ENOMEM;
-            }
-            input_item_CopyOptions( p_current_node->p_item, p_item );
-            input_item_node_AppendItem( p_current_node, p_item );
-            input_item_Release( p_item );
         }
         free( psz_line );
     }
-    return VLC_SUCCESS;
+    return p_item;
 }
 
 /*****************************************************************************
diff --git a/modules/access/sftp.c b/modules/access/sftp.c
index c564786..b708121 100644
--- a/modules/access/sftp.c
+++ b/modules/access/sftp.c
@@ -83,8 +83,7 @@ static block_t* Block( access_t * );
 static int      Seek( access_t *, uint64_t );
 static int      Control( access_t *, int, va_list );
 
-static int      DirControl( access_t *, int, va_list );
-static int      DirRead( access_t *p_access, input_item_node_t *p_current_node );
+static input_item_t* DirRead( access_t *p_access );
 
 struct access_sys_t
 {
@@ -425,10 +424,11 @@ static int Control( access_t* p_access, int i_query, va_list args )
  * Directory access
  *****************************************************************************/
 
-static int DirRead (access_t *p_access, input_item_node_t *p_current_node)
+static input_item_t* DirRead( access_t *p_access )
 {
     access_sys_t *p_sys = p_access->p_sys;
     LIBSSH2_SFTP_ATTRIBUTES attrs;
+    input_item_t *p_item = NULL;
     int err;
     /* Allocate 1024 bytes for file name. Longer names are skipped.
      * libssh2 does not support seeking in directory streams.
@@ -439,9 +439,9 @@ static int DirRead (access_t *p_access, input_item_node_t *p_current_node)
     char *psz_file = malloc( i_size );
 
     if( !psz_file )
-        return VLC_ENOMEM;
+        return NULL;
 
-    while( 0 != ( err = libssh2_sftp_readdir( p_sys->file, psz_file, i_size, &attrs ) ) )
+    while( !p_item && 0 != ( err = libssh2_sftp_readdir( p_sys->file, psz_file, i_size, &attrs ) ) )
     {
         if( err < 0 )
         {
@@ -480,29 +480,25 @@ static int DirRead (access_t *p_access, input_item_node_t *p_current_node)
         free( psz_uri );
 
         int i_type = LIBSSH2_SFTP_S_ISDIR( attrs.permissions ) ? ITEM_TYPE_DIRECTORY : ITEM_TYPE_FILE;
-        input_item_t *p_new = input_item_NewWithTypeExt( psz_full_uri, psz_file,
-                                                         0, NULL, 0, 0, i_type, 1 );
+        p_item = input_item_NewWithTypeExt( psz_full_uri, psz_file,
+                                            0, NULL, 0, 0, i_type, 1 );
 
-        if( p_new == NULL )
+        if( p_item == NULL )
         {
             free( psz_full_uri );
-            continue;
+            break;
         }
 
         /* Here we save on the node the credentials that allowed us to login.
          * That way the user isn't prompted more than once for credentials */
         if( p_sys->psz_password_opt )
-            input_item_AddOption( p_new, p_sys->psz_password_opt, VLC_INPUT_OPTION_TRUSTED );
+            input_item_AddOption( p_item, p_sys->psz_password_opt, VLC_INPUT_OPTION_TRUSTED );
         if( p_sys->psz_username_opt )
-            input_item_AddOption( p_new, p_sys->psz_username_opt, VLC_INPUT_OPTION_TRUSTED );
-
-        input_item_CopyOptions( p_current_node->p_item, p_new );
-        input_item_node_AppendItem( p_current_node, p_new );
+            input_item_AddOption( p_item, p_sys->psz_username_opt, VLC_INPUT_OPTION_TRUSTED );
 
         free( psz_full_uri );
-        input_item_Release( p_new );
     }
 
     free( psz_file );
-    return VLC_SUCCESS;
+    return p_item;
 }
diff --git a/modules/demux/playlist/directory.c b/modules/demux/playlist/directory.c
index 9b7ffc2..f614307 100644
--- a/modules/demux/playlist/directory.c
+++ b/modules/demux/playlist/directory.c
@@ -63,17 +63,37 @@ void Close_Dir ( vlc_object_t *p_this )
 
 static int Demux( demux_t *p_demux )
 {
+    int i_ret = VLC_SUCCESS;
     input_item_t *p_input = GetCurrentItem(p_demux);
     input_item_node_t *p_node = input_item_node_Create( p_input );
+    input_item_t *p_item;
     input_item_Release(p_input);
 
-    if( stream_ReadDir( p_demux->s, p_node ) )
+    while( !i_ret && ( p_item = stream_ReadDir( p_demux->s ) ) )
+    {
+        int i_name_len = p_item->psz_name ? strlen( p_item->psz_name ) : 0;
+
+        /* skip "." and ".." items */
+        if( ( i_name_len == 1 && p_item->psz_name[0] == '.' ) ||
+            ( i_name_len == 2 && p_item->psz_name[0] == '.' &&
+              p_item->psz_name[1] == '.' ) )
+            goto skip_item;
+
+        input_item_CopyOptions( p_node->p_item, p_item );
+        if( !input_item_node_AppendItem( p_node, p_item ) )
+            i_ret = VLC_ENOMEM;
+skip_item:
+        input_item_Release( p_item );
+    }
+
+    if( i_ret )
     {
         msg_Warn( p_demux, "unable to read directory" );
         input_item_node_Delete( p_node );
-        return VLC_EGENERIC;
+        return i_ret;
+    } else
+    {
+        input_item_node_PostAndDelete( p_node );
+        return VLC_SUCCESS;
     }
-
-    input_item_node_PostAndDelete( p_node );
-    return VLC_SUCCESS;
 }
diff --git a/modules/services_discovery/upnp.cpp b/modules/services_discovery/upnp.cpp
index 097a92b..f7f1882 100644
--- a/modules/services_discovery/upnp.cpp
+++ b/modules/services_discovery/upnp.cpp
@@ -57,6 +57,7 @@ struct services_discovery_sys_t
 struct access_sys_t
 {
     UpnpInstanceWrapper* p_upnp;
+    Access::MediaServer* p_server;
 };
 
 UpnpInstanceWrapper* UpnpInstanceWrapper::s_instance;
@@ -523,14 +524,25 @@ int MediaServerList::Callback( Upnp_EventType event_type, void* p_event, void* p
 namespace Access
 {
 
-MediaServer::MediaServer(const char *psz_url, access_t *p_access, input_item_node_t *node)
+MediaServer::MediaServer(const char *psz_url, access_t *p_access)
     : url_( psz_url )
     , access_( p_access )
-    , node_( node )
+    , xmlDocument_( NULL )
+    , containerNodeList_( NULL )
+    , containerNodeIndex_( 0 )
+    , itemNodeList_( NULL )
+    , itemNodeIndex_( 0 )
 {
 }
 
-void MediaServer::addItem(const char *objectID, const char *title )
+MediaServer::~MediaServer()
+{
+    ixmlNodeList_free( containerNodeList_ );
+    ixmlNodeList_free( itemNodeList_ );
+    ixmlDocument_free( xmlDocument_ );
+}
+
+input_item_t* MediaServer::newItem(const char *objectID, const char *title )
 {
     vlc_url_t url;
     vlc_UrlParse( &url, url_.c_str(), '?' );
@@ -540,27 +552,21 @@ void MediaServer::addItem(const char *objectID, const char *title )
                   url.psz_host, url.i_port ? url.i_port : 80, url.psz_path, objectID ) < 0 )
     {
         vlc_UrlClean( &url );
-        return ;
+        return NULL;
     }
     vlc_UrlClean( &url );
 
     input_item_t* p_item = input_item_NewWithTypeExt( psz_url, title, 0, NULL,
                                                       0, -1, ITEM_TYPE_DIRECTORY, 1 );
     free( psz_url);
-    if ( !p_item )
-        return;
-    input_item_CopyOptions( node_->p_item, p_item );
-    input_item_node_AppendItem( node_, p_item );
-    input_item_Release( p_item );
+    return p_item;
 }
 
-void MediaServer::addItem(const char* title, const char*, const char*,
-                          mtime_t duration, const char* psz_url)
+input_item_t* MediaServer::newItem(const char* title, const char*, const char*,
+                                   mtime_t duration, const char* psz_url)
 {
-    input_item_t* p_item = input_item_NewWithTypeExt( psz_url, title, 0, NULL, 0,
-                                                      duration, ITEM_TYPE_FILE, 1 );
-    input_item_node_AppendItem( node_, p_item );
-    input_item_Release( p_item );
+    return input_item_NewWithTypeExt( psz_url, title, 0, NULL, 0,
+                                      duration, ITEM_TYPE_FILE, 1 );
 }
 
 /* Access part */
@@ -665,7 +671,7 @@ browseActionCleanup:
 /*
  * Fetches and parses the UPNP response
  */
-bool MediaServer::fetchContents()
+void MediaServer::fetchContents()
 {
     const char* objectID = "";
     vlc_url_t url;
@@ -688,31 +694,45 @@ bool MediaServer::fetchContents()
     if ( !p_response )
     {
         msg_Err( access_, "No response from browse() action" );
-        return false;
+        return;
     }
 
-    IXML_Document* p_result = parseBrowseResult( p_response );
+    xmlDocument_ = parseBrowseResult( p_response );
 
     ixmlDocument_free( p_response );
 
-    if ( !p_result )
+    if ( !xmlDocument_ )
     {
         msg_Err( access_, "browse() response parsing failed" );
-        return false;
+        return;
     }
 
 #ifndef NDEBUG
-    msg_Dbg( access_, "Got DIDL document: %s", ixmlPrintDocument( p_result ) );
+    msg_Dbg( access_, "Got DIDL document: %s", ixmlPrintDocument( xmlDocument_ ) );
 #endif
 
-    IXML_NodeList* containerNodeList =
-                ixmlDocument_getElementsByTagName( p_result, "container" );
+    containerNodeList_ = ixmlDocument_getElementsByTagName( xmlDocument_, "container" );
+    itemNodeList_ = ixmlDocument_getElementsByTagName( xmlDocument_, "item" );
+}
+
+input_item_t* MediaServer::getNextItem()
+{
+    input_item_t *p_item = NULL;
+
+    if( !xmlDocument_ )
+    {
+        fetchContents();
+        if( !xmlDocument_ )
+            return NULL;
+    }
 
-    if ( containerNodeList )
+    if ( containerNodeList_ )
     {
-        for ( unsigned int i = 0; i < ixmlNodeList_length( containerNodeList ); i++ )
+        for ( ; !p_item && containerNodeIndex_ < ixmlNodeList_length( containerNodeList_ )
+              ; containerNodeIndex_++ )
         {
-            IXML_Element* containerElement = (IXML_Element*)ixmlNodeList_item( containerNodeList, i );
+            IXML_Element* containerElement = (IXML_Element*)ixmlNodeList_item( containerNodeList_,
+                                                                                containerNodeIndex_ );
 
             const char* objectID = ixmlElement_getAttribute( containerElement,
                                                              "id" );
@@ -723,19 +743,18 @@ bool MediaServer::fetchContents()
                                                           "dc:title" );
             if ( !title )
                 continue;
-            addItem(objectID, title);
+            p_item = newItem(objectID, title);
         }
-        ixmlNodeList_free( containerNodeList );
     }
 
-    IXML_NodeList* itemNodeList = ixmlDocument_getElementsByTagName( p_result,
-                                                                     "item" );
-    if ( itemNodeList )
+    if( itemNodeList_ )
     {
-        for ( unsigned int i = 0; i < ixmlNodeList_length( itemNodeList ); i++ )
+        for ( ; !p_item && itemNodeIndex_ < ixmlNodeList_length( itemNodeList_ )
+              ; itemNodeIndex_++ )
         {
             IXML_Element* itemElement =
-                        ( IXML_Element* )ixmlNodeList_item( itemNodeList, i );
+                        ( IXML_Element* )ixmlNodeList_item( itemNodeList_,
+                                                            itemNodeIndex_ );
 
             const char* objectID =
                         ixmlElement_getAttribute( itemElement, "id" );
@@ -781,24 +800,19 @@ bool MediaServer::fetchContents()
                                                           i_seconds );
                 }
 
-                addItem( title, objectID, psz_subtitles, i_duration, psz_resource_url );
+                p_item = newItem( title, objectID, psz_subtitles, i_duration,
+                                  psz_resource_url );
             }
             ixmlNodeList_free( p_resource_list );
         }
-        ixmlNodeList_free( itemNodeList );
     }
 
-    ixmlDocument_free( p_result );
-    return true;
+    return p_item;
 }
 
-static int ReadDirectory( access_t *p_access, input_item_node_t* p_node )
+static input_item_t* ReadDirectory( access_t *p_access )
 {
-    MediaServer server( p_access->psz_location, p_access, p_node );
-
-    if ( !server.fetchContents() )
-        return VLC_EGENERIC;
-    return VLC_SUCCESS;
+    return p_access->p_sys->p_server->getNextItem();
 }
 
 static int Open( vlc_object_t *p_this )
@@ -809,9 +823,17 @@ static int Open( vlc_object_t *p_this )
         return VLC_ENOMEM;
 
     p_access->p_sys = p_sys;
+    p_sys->p_server = new(std::nothrow) MediaServer( p_access->psz_location,
+                                                     p_access );
+    if ( !p_sys->p_server )
+    {
+        delete p_sys;
+        return VLC_EGENERIC;
+    }
     p_sys->p_upnp = UpnpInstanceWrapper::get( p_this, NULL, NULL );
     if ( !p_sys->p_upnp )
     {
+        delete p_sys->p_server;
         delete p_sys;
         return VLC_EGENERIC;
     }
@@ -825,6 +847,7 @@ static void Close( vlc_object_t* p_this )
 {
     access_t* p_access = (access_t*)p_this;
     p_access->p_sys->p_upnp->release( false );
+    delete p_access->p_sys->p_server;
     delete p_access->p_sys;
 }
 
diff --git a/modules/services_discovery/upnp.hpp b/modules/services_discovery/upnp.hpp
index 11e3a5a..db37f3d 100644
--- a/modules/services_discovery/upnp.hpp
+++ b/modules/services_discovery/upnp.hpp
@@ -119,15 +119,17 @@ namespace Access
 class MediaServer
 {
 public:
-    MediaServer( const char* psz_url, access_t* p_access, input_item_node_t* node );
-    bool fetchContents();
+    MediaServer( const char* psz_url, access_t* p_access );
+    ~MediaServer();
+    input_item_t* getNextItem();
 
 private:
     MediaServer(const MediaServer&);
     MediaServer& operator=(const MediaServer&);
 
-    void addItem(const char* objectID, const char* title);
-    void addItem(const char* title, const char* psz_objectID, const char* psz_subtitles, mtime_t duration, const char* psz_url );
+    void fetchContents();
+    input_item_t* newItem(const char* objectID, const char* title);
+    input_item_t* newItem(const char* title, const char* psz_objectID, const char* psz_subtitles, mtime_t duration, const char* psz_url );
 
     IXML_Document* _browseAction(const char*, const char*,
             const char*, const char*, const char* );
@@ -135,7 +137,11 @@ private:
 private:
     const std::string url_;
     access_t* access_;
-    input_item_node_t* node_;
+    IXML_Document* xmlDocument_;
+    IXML_NodeList* containerNodeList_;
+    unsigned int   containerNodeIndex_;
+    IXML_NodeList* itemNodeList_;
+    unsigned int   itemNodeIndex_;
 };
 
 }
diff --git a/src/input/stream.c b/src/input/stream.c
index 03c858e..e220742 100644
--- a/src/input/stream.c
+++ b/src/input/stream.c
@@ -192,7 +192,7 @@ static void AStreamPrebufferStream( stream_t *s );
 static int  AReadStream( stream_t *s, void *p_read, unsigned int i_read );
 
 /* ReadDir */
-static int  AStreamReadDir( stream_t *s, input_item_node_t *p_node );
+static input_item_t *AStreamReadDir( stream_t *s );
 
 /* Common */
 static int  AStreamGenericError( ) { return VLC_EGENERIC; }
@@ -1840,11 +1840,11 @@ static int ASeek( stream_t *s, uint64_t i_pos )
     return p_access->pf_seek( p_access, i_pos );
 }
 
-static int AStreamReadDir( stream_t *s, input_item_node_t *p_node )
+static input_item_t *AStreamReadDir( stream_t *s )
 {
     access_t *p_access = s->p_sys->p_access;
 
-    return p_access->pf_readdir( p_access, p_node );
+    return p_access->pf_readdir( p_access );
 }
 
 /**
@@ -1979,10 +1979,11 @@ block_t *stream_BlockRemaining( stream_t *s, int i_max_size )
 }
 
 /**
- * Returns a node containing all the input_item of the directory pointer by
- * this stream. returns VLC_SUCCESS on success.
+ * Read the next input_item_t from the directory stream. It returns the next
+ * input item on success or NULL in case of error or end of stream. The item
+ * must be released with input_item_Release.
  */
-int stream_ReadDir( stream_t *s, input_item_node_t *p_node )
+input_item_t *stream_ReadDir( stream_t *s )
 {
-    return s->pf_readdir( s, p_node );
+    return s->pf_readdir( s );
 }
diff --git a/src/input/stream_filter.c b/src/input/stream_filter.c
index 368b56d..e0ce629 100644
--- a/src/input/stream_filter.c
+++ b/src/input/stream_filter.c
@@ -128,8 +128,8 @@ static void StreamDelete( stream_t *s )
     stream_CommonDelete( s );
 }
 
-int stream_FilterDefaultReadDir( stream_t *s, input_item_node_t *p_node )
+input_item_t *stream_FilterDefaultReadDir( stream_t *s )
 {
     assert( s->p_source != NULL );
-    return stream_ReadDir( s->p_source, p_node );
+    return stream_ReadDir( s->p_source );
 }
-- 
2.1.4




More information about the vlc-devel mailing list