demux.c 8.66 KB
Newer Older
1
/*****************************************************************************
2
 * demux.c :  Lua playlist demux module
3
 *****************************************************************************
4
 * Copyright (C) 2007-2008 the VideoLAN team
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
 * $Id$
 *
 * Authors: Antoine Cellerier <dionoea at videolan tod org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
27 28 29 30
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

31
#include <assert.h>
32 33 34 35 36
#include <stdlib.h>
#include <string.h>

#include <vlc_common.h>
#include <vlc_access.h>
37

38
#include "vlc.h"
39
#include "libs.h"
40 41

/*****************************************************************************
42
 * Demux specific functions
43
 *****************************************************************************/
44
struct vlclua_playlist
45 46
{
    lua_State *L;
47 48 49
    char *filename;
    char *access;
    const char *path;
50 51 52 53
};

static int vlclua_demux_peek( lua_State *L )
{
54
    stream_t *s = (stream_t *)vlclua_get_this(L);
55
    int n = luaL_checkint( L, 1 );
56
    const uint8_t *p_peek;
57

58
    ssize_t val = vlc_stream_Peek(s->s, &p_peek, n);
59 60
    if (val > 0)
        lua_pushlstring(L, (const char *)p_peek, val);
61 62
    else
        lua_pushnil( L );
63 64 65 66 67
    return 1;
}

static int vlclua_demux_read( lua_State *L )
{
68
    stream_t *s = (stream_t *)vlclua_get_this(L);
69
    int n = luaL_checkint( L, 1 );
70
    char *buf = malloc(n);
71

72
    if (buf != NULL)
73
    {
74
        ssize_t val = vlc_stream_Read(s->s, buf, n);
75 76 77 78 79
        if (val > 0)
            lua_pushlstring(L, buf, val);
        else
            lua_pushnil( L );
        free(buf);
80 81 82 83
    }
    else
        lua_pushnil( L );

84 85 86 87 88
    return 1;
}

static int vlclua_demux_readline( lua_State *L )
{
89
    stream_t *s = (stream_t *)vlclua_get_this(L);
90
    char *line = vlc_stream_ReadLine(s->s);
91 92

    if (line != NULL)
93
    {
94 95
        lua_pushstring(L, line);
        free(line);
96 97 98
    }
    else
        lua_pushnil( L );
99

100 101 102
    return 1;
}

103 104 105
/*****************************************************************************
 *
 *****************************************************************************/
106
/* Functions to register */
107
static const luaL_Reg p_reg[] =
108 109 110 111 112 113
{
    { "peek", vlclua_demux_peek },
    { NULL, NULL }
};

/* Functions to register for parse() function call only */
114
static const luaL_Reg p_reg_parse[] =
115 116 117 118 119 120 121 122 123 124
{
    { "read", vlclua_demux_read },
    { "readline", vlclua_demux_readline },
    { NULL, NULL }
};

/*****************************************************************************
 * Called through lua_scripts_batch_execute to call 'probe' on
 * the script pointed by psz_filename.
 *****************************************************************************/
125 126
static int probe_luascript(vlc_object_t *obj, const char *filename,
                           const luabatch_context_t *ctx)
127
{
128 129
    stream_t *s = (stream_t *)obj;
    struct vlclua_playlist *sys = s->p_sys;
130

131 132 133
    /* Initialise Lua state structure */
    lua_State *L = luaL_newstate();
    if( !L )
134 135 136
        return VLC_ENOMEM;

    sys->L = L;
137 138 139 140

    /* Load Lua libraries */
    luaL_openlibs( L ); /* FIXME: Don't open all the libs? */

141
    vlclua_set_this(L, s);
142
    luaL_register_namespace( L, "vlc", p_reg );
143 144
    luaopen_msg( L );
    luaopen_strings( L );
145
    luaopen_stream( L );
146
    luaopen_variables( L );
Antoine Cellerier's avatar
Antoine Cellerier committed
147
    luaopen_xml( L );
148 149 150 151 152

    if (sys->path != NULL)
        lua_pushstring(L, sys->path);
    else
        lua_pushnil(L);
153
    lua_setfield( L, -2, "path" );
154 155 156 157 158

    if (sys->access != NULL)
        lua_pushstring(L, sys->access);
    else
        lua_pushnil(L);
159 160 161
    lua_setfield( L, -2, "access" );

    lua_pop( L, 1 );
162

163
    /* Setup the module search path */
164
    if (vlclua_add_modules_path(L, filename))
165
    {
166
        msg_Warn(s, "error setting the module search path for %s", filename);
167 168 169
        goto error;
    }

170
    /* Load and run the script(s) */
171
    if (vlclua_dofile(VLC_OBJECT(s), L, filename))
172
    {
173 174
        msg_Warn(s, "error loading script %s: %s", filename,
                 lua_tostring(L, lua_gettop(L)));
175
        goto error;
176 177 178
    }

    lua_getglobal( L, "probe" );
179
    if( !lua_isfunction( L, -1 ) )
180
    {
181 182
        msg_Warn(s, "error running script %s: function %s(): %s",
                 filename, "probe", "not found");
183
        goto error;
184 185 186 187
    }

    if( lua_pcall( L, 0, 1, 0 ) )
    {
188 189
        msg_Warn(s, "error running script %s: function %s(): %s",
                 filename, "probe", lua_tostring(L, lua_gettop(L)));
190
        goto error;
191 192 193 194 195 196
    }

    if( lua_gettop( L ) )
    {
        if( lua_toboolean( L, 1 ) )
        {
197 198
            msg_Dbg(s, "Lua playlist script %s's "
                    "probe() function was successful", filename );
199
            lua_pop( L, 1 );
200
            sys->filename = strdup(filename);
201
            return VLC_SUCCESS;
202 203
        }
    }
204

205
    (void) ctx;
206
error:
207
    lua_pop( L, 1 );
208
    lua_close(sys->L);
209 210 211
    return VLC_EGENERIC;
}

212
static int ReadDir(stream_t *s, input_item_node_t *node)
213
{
214 215
    struct vlclua_playlist *sys = s->p_sys;
    lua_State *L = sys->L;
216

217
    luaL_register_namespace( L, "vlc", p_reg_parse );
218

219
    lua_getglobal( L, "parse" );
220

221 222 223 224 225 226 227 228 229 230 231 232 233
    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;
    }
234

235 236 237 238 239
    if (!lua_gettop(L))
    {
        msg_Err(s, "script went completely foobar");
        return VLC_ENOITEM;
    }
240

241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
    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;
271 272
}

273 274 275 276
/*****************************************************************************
 * Import_LuaPlaylist: main import function
 *****************************************************************************/
int Import_LuaPlaylist(vlc_object_t *obj)
277
{
278 279
    if( lua_Disabled( obj ) )
        return VLC_EGENERIC;
280

281
    stream_t *s = (stream_t *)obj;
282
    if( s->s->pf_readdir != NULL )
283 284 285
        return VLC_EGENERIC;

    struct vlclua_playlist *sys = malloc(sizeof (*sys));
286 287
    if (unlikely(sys == NULL))
        return VLC_ENOMEM;
288

289 290 291
    s->p_sys = sys;
    sys->access = NULL;
    sys->path = NULL;
292

293 294 295 296 297 298 299 300 301
    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;
        }
302 303
    }

304 305 306
    int ret = vlclua_scripts_batch_execute(VLC_OBJECT(s), "playlist",
                                           probe_luascript, NULL);
    if (ret != VLC_SUCCESS)
307
    {
308 309 310
        free(sys->access);
        free(sys);
        return ret;
311 312
    }

313 314 315
    s->pf_readdir = ReadDir;
    s->pf_control = access_vaDirectoryControlHelper;
    return VLC_SUCCESS;
316 317
}

318
void Close_LuaPlaylist(vlc_object_t *obj)
319
{
320 321 322 323 324 325 326 327
    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);
328
}