[vlc-devel] [vlc-commits] lua: convert playlist parser to stream filter

Thomas Guillem thomas at gllm.fr
Wed May 17 16:54:24 CEST 2017


Hello,

This commit causes every playlist lua scripts to be probed for all
inputs. There are probed from stream_FilterAutoNew() in InputDemuxNew().
This cause a massive performance issue when preparsing a lot of files,
this can double or triple the parse duration for one file.

Also, since lua playlists normally only check the url, a matching lua
"stream-filter" will be added 16 times (because of the loop inside
stream_FilterAutoNew()) and playback will fail since the first filter
will consume the stream data. See
http://git.videolan.org/gitweb.cgi?p=vlc.git;a=blob;f=share/lua/playlist/youtube.lua;h=7b63f91fa84a5da799e4a09a375448661deaf6df;hb=HEAD#l228
for a probe example.

Regards,
Thomas Guillem

On Tue, Apr 18, 2017, at 22:43, Rémi Denis-Courmont wrote:
> vlc | branch: master | Rémi Denis-Courmont <remi at remlab.net> | Tue Apr 18
> 22:35:47 2017 +0300| [ca74826ea7623ba3788c8b5eeb3f8b31dda37012] |
> committer: Rémi Denis-Courmont
> 
> lua: convert playlist parser to stream filter
> 
> ReadDir() is more logical and simpler than Demux() here.
> 
> > http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=ca74826ea7623ba3788c8b5eeb3f8b31dda37012
> ---
> 
>  modules/lua/demux.c | 259
>  ++++++++++++++++++++++++++++++----------------------
>  modules/lua/vlc.c   |  62 +------------
>  modules/lua/vlc.h   |   3 -
>  3 files changed, 150 insertions(+), 174 deletions(-)
> 
> diff --git a/modules/lua/demux.c b/modules/lua/demux.c
> index 38fee11a43..92224961d2 100644
> --- a/modules/lua/demux.c
> +++ b/modules/lua/demux.c
> @@ -29,35 +29,35 @@
>  #endif
>  
>  #include <assert.h>
> +#include <stdlib.h>
> +#include <string.h>
> +
> +#include <vlc_common.h>
> +#include <vlc_access.h>
>  
>  #include "vlc.h"
>  #include "libs.h"
>  
> -
> -/*****************************************************************************
> - * Local prototypes
> -
> *****************************************************************************/
> -static int Demux( demux_t *p_demux );
> -static int Control( demux_t *p_demux, int i_query, va_list args );
> -
>  /*****************************************************************************
>   * Demux specific functions
>   *****************************************************************************/
> -struct demux_sys_t
> +struct vlclua_playlist
>  {
>      lua_State *L;
> -    char *psz_filename;
> +    char *filename;
> +    char *access;
> +    const char *path;
>  };
>  
>  static int vlclua_demux_peek( lua_State *L )
>  {
> -    demux_t *p_demux = (demux_t *)vlclua_get_this( L );
> +    stream_t *s = (stream_t *)vlclua_get_this(L);
>      int n = luaL_checkint( L, 1 );
>      const uint8_t *p_peek;
>  
> -    int i_peek = vlc_stream_Peek( p_demux->s, &p_peek, n );
> -    if( i_peek > 0 )
> -        lua_pushlstring( L, (const char *)p_peek, i_peek );
> +    ssize_t val = vlc_stream_Peek(s->p_source, &p_peek, n);
> +    if (val > 0)
> +        lua_pushlstring(L, (const char *)p_peek, val);
>      else
>          lua_pushnil( L );
>      return 1;
> @@ -65,16 +65,16 @@ static int vlclua_demux_peek( lua_State *L )
>  
>  static int vlclua_demux_read( lua_State *L )
>  {
> -    demux_t *p_demux = (demux_t *)vlclua_get_this( L );
> +    stream_t *s = (stream_t *)vlclua_get_this(L);
>      const uint8_t *p_read;
>      int n = luaL_checkint( L, 1 );
> -    int i_read = vlc_stream_Peek( p_demux->s, &p_read, n );
> +    ssize_t val = vlc_stream_Peek(s->p_source, &p_read, n);
>  
> -    if( i_read > 0 )
> +    if (val > 0)
>      {
> -        lua_pushlstring( L, (const char *)p_read, i_read );
> -        int i_seek = vlc_stream_Read( p_demux->s, NULL, i_read );
> -        assert( i_read == i_seek );
> +        lua_pushlstring(L, (const char *)p_read, val);
> +        int i_seek = vlc_stream_Read(s->p_source, NULL, val);
> +        assert(val == i_seek );
>      }
>      else
>          lua_pushnil( L );
> @@ -84,17 +84,17 @@ static int vlclua_demux_read( lua_State *L )
>  
>  static int vlclua_demux_readline( lua_State *L )
>  {
> -    demux_t *p_demux = (demux_t *)vlclua_get_this( L );
> -    char *psz_line = vlc_stream_ReadLine( p_demux->s );
> -    if( psz_line )
> +    stream_t *s = (stream_t *)vlclua_get_this(L);
> +    char *line = vlc_stream_ReadLine(s->p_source);
> +
> +    if (line != NULL)
>      {
> -        lua_pushstring( L, psz_line );
> -        free( psz_line );
> +        lua_pushstring(L, line);
> +        free(line);
>      }
>      else
> -    {
>          lua_pushnil( L );
> -    }
> +
>      return 1;
>  }
>  
> @@ -120,70 +120,71 @@ static const luaL_Reg p_reg_parse[] =
>   * Called through lua_scripts_batch_execute to call 'probe' on
>   * the script pointed by psz_filename.
>   *****************************************************************************/
> -static int probe_luascript( vlc_object_t *p_this, const char *
> psz_filename,
> -                            const luabatch_context_t *p_context )
> +static int probe_luascript(vlc_object_t *obj, const char *filename,
> +                           const luabatch_context_t *ctx)
>  {
> -    VLC_UNUSED(p_context);
> -    demux_t * p_demux = (demux_t *)p_this;
> -
> -    p_demux->p_sys->psz_filename = strdup(psz_filename);
> +    stream_t *s = (stream_t *)obj;
> +    struct vlclua_playlist *sys = s->p_sys;
>  
>      /* Initialise Lua state structure */
>      lua_State *L = luaL_newstate();
>      if( !L )
> -    {
> -        msg_Err( p_demux, "Could not create new Lua State" );
> -        goto error;
> -    }
> -    p_demux->p_sys->L = L;
> +        return VLC_ENOMEM;
> +
> +    sys->L = L;
>  
>      /* Load Lua libraries */
>      luaL_openlibs( L ); /* FIXME: Don't open all the libs? */
>  
> -    vlclua_set_this( L, p_demux );
> +    vlclua_set_this(L, s);
>      luaL_register_namespace( L, "vlc", p_reg );
>      luaopen_msg( L );
>      luaopen_strings( L );
>      luaopen_stream( L );
>      luaopen_variables( L );
>      luaopen_xml( L );
> -    lua_pushstring( L, p_demux->psz_location );
> +
> +    if (sys->path != NULL)
> +        lua_pushstring(L, sys->path);
> +    else
> +        lua_pushnil(L);
>      lua_setfield( L, -2, "path" );
> -    lua_pushstring( L, p_demux->psz_access );
> +
> +    if (sys->access != NULL)
> +        lua_pushstring(L, sys->access);
> +    else
> +        lua_pushnil(L);
>      lua_setfield( L, -2, "access" );
>  
>      lua_pop( L, 1 );
>  
>      /* Setup the module search path */
> -    if( vlclua_add_modules_path( L, psz_filename ) )
> +    if (vlclua_add_modules_path(L, filename))
>      {
> -        msg_Warn( p_demux, "Error while setting the module search path
> for %s",
> -                  psz_filename );
> +        msg_Warn(s, "error setting the module search path for %s",
> filename);
>          goto error;
>      }
>  
>      /* Load and run the script(s) */
> -    if( vlclua_dofile( VLC_OBJECT(p_demux), L, psz_filename ) )
> +    if (vlclua_dofile(VLC_OBJECT(s), L, filename))
>      {
> -        msg_Warn( p_demux, "Error loading script %s: %s", psz_filename,
> -                  lua_tostring( L, lua_gettop( L ) ) );
> +        msg_Warn(s, "error loading script %s: %s", filename,
> +                 lua_tostring(L, lua_gettop(L)));
>          goto error;
>      }
>  
>      lua_getglobal( L, "probe" );
> -
>      if( !lua_isfunction( L, -1 ) )
>      {
> -        msg_Warn( p_demux, "Error while running script %s, "
> -                  "function probe() not found", psz_filename );
> +        msg_Warn(s, "error running script %s: function %s(): %s",
> +                 filename, "probe", "not found");
>          goto error;
>      }
>  
>      if( lua_pcall( L, 0, 1, 0 ) )
>      {
> -        msg_Warn( p_demux, "Error while running script %s, "
> -                  "function probe(): %s", psz_filename,
> -                  lua_tostring( L, lua_gettop( L ) ) );
> +        msg_Warn(s, "error running script %s: function %s(): %s",
> +                 filename, "probe", lua_tostring(L, lua_gettop(L)));
>          goto error;
>      }
>  
> @@ -191,92 +192,130 @@ static int probe_luascript( vlc_object_t *p_this,
> const char * psz_filename,
>      {
>          if( lua_toboolean( L, 1 ) )
>          {
> -            msg_Dbg( p_demux, "Lua playlist script %s's "
> -                     "probe() function was successful", psz_filename );
> +            msg_Dbg(s, "Lua playlist script %s's "
> +                    "probe() function was successful", filename );
>              lua_pop( L, 1 );
> +            sys->filename = strdup(filename);
>              return VLC_SUCCESS;
>          }
>      }
>  
> +    (void) ctx;
>  error:
>      lua_pop( L, 1 );
> -    lua_close( p_demux->p_sys->L );
> -    p_demux->p_sys->L = NULL;
> -    FREENULL( p_demux->p_sys->psz_filename );
> +    lua_close(sys->L);
>      return VLC_EGENERIC;
>  }
>  
> -/*****************************************************************************
> - * Import_LuaPlaylist: main import function
> -
> *****************************************************************************/
> -int Import_LuaPlaylist( vlc_object_t *p_this )
> +static int ReadDir(stream_t *s, input_item_node_t *node)
>  {
> -    demux_t *p_demux = (demux_t *)p_this;
> -    int ret;
> +    struct vlclua_playlist *sys = s->p_sys;
> +    lua_State *L = sys->L;
>  
> -    p_demux->p_sys = calloc( 1, sizeof( demux_sys_t ) );
> -    if( !p_demux->p_sys )
> -        return VLC_ENOMEM;
> +    luaL_register_namespace( L, "vlc", p_reg_parse );
>  
> -    p_demux->pf_control = Control;
> -    p_demux->pf_demux = Demux;
> +    lua_getglobal( L, "parse" );
>  
> -    ret = vlclua_scripts_batch_execute( p_this, "playlist",
> -                                        &probe_luascript, NULL );
> -    if( ret )
> -        Close_LuaPlaylist( p_this );
> -    return ret;
> -}
> +    if( !lua_isfunction( L, -1 ) )
> +    {
> +        msg_Warn(s, "error running script %s: function %s(): %s",
> +                 sys->filename, "parse", "not found");
> +        return VLC_ENOITEM;
> +    }
> +
> +    if( lua_pcall( L, 0, 1, 0 ) )
> +    {
> +        msg_Warn(s, "error running script %s: function %s(): %s",
> +                 sys->filename, "parse", lua_tostring(L,
> lua_gettop(L)));
> +        return VLC_ENOITEM;
> +    }
>  
> +    if (!lua_gettop(L))
> +    {
> +        msg_Err(s, "script went completely foobar");
> +        return VLC_ENOITEM;
> +    }
>  
> -/*****************************************************************************
> - * Deactivate: frees unused data
> -
> *****************************************************************************/
> -void Close_LuaPlaylist( vlc_object_t *p_this )
> -{
> -    demux_t *p_demux = (demux_t *)p_this;
> -    if( p_demux->p_sys->L )
> -        lua_close( p_demux->p_sys->L );
> -    free( p_demux->p_sys->psz_filename );
> -    free( p_demux->p_sys );
> +    if (!lua_istable(L, -1))
> +    {
> +        msg_Warn(s, "Playlist should be a table.");
> +        return VLC_ENOITEM;
> +    }
> +
> +    lua_pushnil(L);
> +
> +    /* playlist nil */
> +    while (lua_next(L, -2))
> +    {
> +        input_item_t *item = vlclua_read_input_item(VLC_OBJECT(s), L);
> +        if (item != NULL)
> +        {
> +            /* copy the original URL to the meta data,
> +             * if "URL" is still empty */
> +            char *url = input_item_GetURL(item);
> +            if (url == NULL && s->psz_url != NULL)
> +                input_item_SetURL(item, s->psz_url);
> +            free(url);
> +
> +            input_item_node_AppendItem(node, item);
> +            input_item_Release(item);
> +        }
> +        /* pop the value, keep the key for the next lua_next() call */
> +        lua_pop(L, 1);
> +    }
> +    /* playlist */
> +
> +    return VLC_SUCCESS;
>  }
>  
> -static int Demux( demux_t *p_demux )
> +/*****************************************************************************
> + * Import_LuaPlaylist: main import function
> +
> *****************************************************************************/
> +int Import_LuaPlaylist(vlc_object_t *obj)
>  {
> -    lua_State *L = p_demux->p_sys->L;
> -    char *psz_filename = p_demux->p_sys->psz_filename;
> -
> -    input_item_t *p_current_input = input_GetItem( p_demux->p_input );
> +    stream_t *s = (stream_t *)obj;
> +    struct vlclua_playlist *sys = malloc(sizeof (*sys));
>  
> -    luaL_register_namespace( L, "vlc", p_reg_parse );
> +    if (unlikely(sys == NULL))
> +        return VLC_ENOMEM;
>  
> -    lua_getglobal( L, "parse" );
> +    s->p_sys = sys;
> +    sys->access = NULL;
> +    sys->path = NULL;
>  
> -    if( !lua_isfunction( L, -1 ) )
> -    {
> -        msg_Warn( p_demux, "Error while running script %s, "
> -                  "function parse() not found", psz_filename );
> -        return VLC_DEMUXER_EGENERIC;
> +    if (s->psz_url != NULL)
> +    {   /* Backward compatibility hack: Lua scripts expect the URI
> scheme and
> +         * the rest of the URI separately. */
> +        const char *p = strstr(s->psz_url, "://");
> +        if (p != NULL)
> +        {
> +            sys->access = strndup(s->psz_url, p - s->psz_url);
> +            sys->path = p + 3;
> +        }
>      }
>  
> -    if( lua_pcall( L, 0, 1, 0 ) )
> +    int ret = vlclua_scripts_batch_execute(VLC_OBJECT(s), "playlist",
> +                                           probe_luascript, NULL);
> +    if (ret != VLC_SUCCESS)
>      {
> -        msg_Warn( p_demux, "Error while running script %s, "
> -                  "function parse(): %s", psz_filename,
> -                  lua_tostring( L, lua_gettop( L ) ) );
> -        return VLC_DEMUXER_EGENERIC;
> +        free(sys->access);
> +        free(sys);
> +        return ret;
>      }
>  
> -    if( lua_gettop( L ) )
> -        vlclua_playlist_add_internal( p_demux, L, p_current_input );
> -    else
> -        msg_Err( p_demux, "Script went completely foobar" );
> -
> -    return VLC_DEMUXER_EOF;
> +    s->pf_readdir = ReadDir;
> +    s->pf_control = access_vaDirectoryControlHelper;
> +    return VLC_SUCCESS;
>  }
>  
> -static int Control( demux_t *p_demux, int i_query, va_list args )
> +void Close_LuaPlaylist(vlc_object_t *obj)
>  {
> -    VLC_UNUSED(p_demux); VLC_UNUSED(i_query); VLC_UNUSED(args);
> -    return VLC_EGENERIC;
> +    stream_t *s = (stream_t *)obj;
> +    struct vlclua_playlist *sys = s->p_sys;
> +
> +    free(sys->filename);
> +    assert(sys->L != NULL);
> +    lua_close(sys->L);
> +    free(sys->access);
> +    free(sys);
>  }
> diff --git a/modules/lua/vlc.c b/modules/lua/vlc.c
> index ed95ac039b..441113fe61 100644
> --- a/modules/lua/vlc.c
> +++ b/modules/lua/vlc.c
> @@ -147,7 +147,7 @@ vlc_module_begin ()
>          add_shortcut( "luaplaylist" )
>          set_shortname( N_("Lua Playlist") )
>          set_description( N_("Lua Playlist Parser Interface") )
> -        set_capability( "demux", 2 )
> +        set_capability( "stream_filter", 2 )
>          set_callbacks( Import_LuaPlaylist, Close_LuaPlaylist )
>  
>      add_submodule ()
> @@ -543,66 +543,6 @@ out:
>      return item;
>  }
>  
> -#undef vlclua_playlist_add_internal
> -void vlclua_playlist_add_internal(vlc_object_t *obj, lua_State *L,
> -                                  input_item_t *parent)
> -{
> -    bool post = false;
> -
> -    /* playlist */
> -    if (!lua_istable(L, -1))
> -    {
> -        msg_Warn(obj, "Playlist should be a table.");
> -        return;
> -    }
> -
> -    input_item_node_t *node = input_item_node_Create(parent);
> -
> -    lua_pushnil(L);
> -
> -    /* playlist nil */
> -    while (lua_next(L, -2))
> -    {
> -        input_item_t *item = vlclua_read_input_item(obj, L);
> -        if (item != NULL)
> -        {
> -            /* copy the original URL to the meta data,
> -             * if "URL" is still empty */
> -            char *url = input_item_GetURL(item);
> -            if (url == NULL)
> -            {
> -                url = input_item_GetURI(parent);
> -                if (likely(url != NULL))
> -                {
> -                    EnsureUTF8(url);
> -                    msg_Dbg(obj, "meta-URL: %s", url);
> -                    input_item_SetURL(item, url);
> -                }
> -            }
> -            free(url);
> -
> -            input_item_CopyOptions(item, parent);
> -
> -            if (likely(node != NULL)) /* Add to node */
> -                input_item_node_AppendItem(node, item);
> -
> -            input_item_Release(item);
> -            post = true;
> -        }
> -        /* pop the value, keep the key for the next lua_next() call */
> -        lua_pop(L, 1);
> -    }
> -    /* playlist */
> -
> -    if (likely(node != NULL))
> -    {
> -        if (post)
> -            input_item_node_PostAndDelete(node);
> -        else
> -            input_item_node_Delete(node);
> -    }
> -}
> -
>  static int vlc_sd_probe_Open( vlc_object_t *obj )
>  {
>      vlc_dictionary_t name_d;
> diff --git a/modules/lua/vlc.h b/modules/lua/vlc.h
> index 7b979cd4f1..141f331329 100644
> --- a/modules/lua/vlc.h
> +++ b/modules/lua/vlc.h
> @@ -182,9 +182,6 @@ void vlclua_read_custom_meta_data( vlc_object_t *,
> lua_State *,
>  #define vlclua_read_custom_meta_data( a, b, c )
>  vlclua_read_custom_meta_data( VLC_OBJECT( a ), b, c )
>  
>  input_item_t *vlclua_read_input_item(vlc_object_t *, lua_State *);
> -void vlclua_playlist_add_internal(vlc_object_t *, lua_State *,
> input_item_t *);
> -#define vlclua_playlist_add_internal( a, b, c ) \
> -        vlclua_playlist_add_internal( VLC_OBJECT( a ), b, c )
>  
>  int vlclua_add_modules_path( lua_State *, const char *psz_filename );
>  
> 
> _______________________________________________
> vlc-commits mailing list
> vlc-commits at videolan.org
> https://mailman.videolan.org/listinfo/vlc-commits


More information about the vlc-devel mailing list