1 /*****************************************************************************
2  * input.c
3  *****************************************************************************
4  * Copyright (C) 2007-2008 the VideoLAN team
5  * $Id: 7490cd5d2688ae9fab8680291e93ad7f9edd3530 $
6  *
7  * Authors: Antoine Cellerier <dionoea at videolan tod org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23 
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #ifndef  _GNU_SOURCE
28 #   define  _GNU_SOURCE
29 #endif
30 
31 #ifdef HAVE_CONFIG_H
32 # include "config.h"
33 #endif
34 
35 #include <vlc_common.h>
36 #include <vlc_meta.h>
37 #include <vlc_url.h>
38 #include <vlc_playlist.h>
39 
40 #include <assert.h>
41 
42 #include "../vlc.h"
43 #include "input.h"
44 #include "../libs.h"
45 #include "../extension.h"
46 
47 static input_item_t* vlclua_input_item_get_internal( lua_State *L );
48 
vlclua_get_input_internal(lua_State * L)49 input_thread_t * vlclua_get_input_internal( lua_State *L )
50 {
51     extension_t *p_extension = vlclua_extension_get( L );
52     if( p_extension )
53     {
54         input_thread_t *p_input = p_extension->p_sys->p_input;
55         if( p_input )
56         {
57             vlc_object_hold(p_input);
58             return p_input;
59         }
60     }
61 
62     playlist_t *p_playlist = vlclua_get_playlist_internal( L );
63     if( p_playlist != NULL )
64     {
65         input_thread_t *p_input = playlist_CurrentInput( p_playlist );
66         if( p_input )
67             return p_input;
68     }
69 
70     return NULL;
71 }
72 
vlclua_input_item_info(lua_State * L)73 static int vlclua_input_item_info( lua_State *L )
74 {
75     input_item_t *p_item = vlclua_input_item_get_internal( L );
76     int i_cat;
77     int i;
78     i_cat = p_item->i_categories;
79     lua_createtable( L, 0, i_cat );
80     for( i = 0; i < i_cat; i++ )
81     {
82         info_category_t *p_category = p_item->pp_categories[i];
83         int i_infos = p_category->i_infos;
84         int j;
85         lua_pushstring( L, p_category->psz_name );
86         lua_createtable( L, 0, i_infos );
87         for( j = 0; j < i_infos; j++ )
88         {
89             info_t *p_info = p_category->pp_infos[j];
90             lua_pushstring( L, p_info->psz_name );
91             lua_pushstring( L, p_info->psz_value );
92             lua_settable( L, -3 );
93         }
94         lua_settable( L, -3 );
95     }
96     return 1;
97 }
98 
vlclua_input_is_playing(lua_State * L)99 static int vlclua_input_is_playing( lua_State *L )
100 {
101     input_thread_t * p_input = vlclua_get_input_internal( L );
102     lua_pushboolean( L, !!p_input );
103     if( p_input )
104         vlc_object_release( p_input );
105     return 1;
106 }
107 
vlclua_input_metas_internal(lua_State * L,input_item_t * p_item)108 static int vlclua_input_metas_internal( lua_State *L, input_item_t *p_item )
109 {
110     if( !p_item )
111     {
112         lua_pushnil( L );
113         return 1;
114     }
115 
116     lua_newtable( L );
117     const char *psz_meta;
118 
119     char* psz_uri = input_item_GetURI( p_item );
120     char* psz_filename = psz_uri ? strrchr( psz_uri, '/' ) : NULL;
121 
122     if( psz_filename && psz_filename[1] == '\0' )
123     {
124         /* trailing slash, get the preceeding data */
125         psz_filename[0] = '\0';
126         psz_filename = strrchr( psz_uri, '/' );
127     }
128 
129     if( psz_filename )
130     {
131         /* url decode, without leading slash */
132         psz_filename = vlc_uri_decode( psz_filename + 1 );
133     }
134 
135     lua_pushstring( L, psz_filename );
136     lua_setfield( L, -2, "filename" );
137 
138     free( psz_uri );
139 
140 #define PUSH_META( n, m ) \
141     psz_meta = vlc_meta_Get( p_item->p_meta, vlc_meta_ ## n ); \
142     lua_pushstring( L, psz_meta ); \
143     lua_setfield( L, -2, m )
144 
145     vlc_mutex_lock(&p_item->lock);
146 
147     if (p_item->p_meta)
148     {
149         PUSH_META( Title, "title" );
150         PUSH_META( Artist, "artist" );
151         PUSH_META( Genre, "genre" );
152         PUSH_META( Copyright, "copyright" );
153         PUSH_META( Album, "album" );
154         PUSH_META( TrackNumber, "track_number" );
155         PUSH_META( Description, "description" );
156         PUSH_META( Rating, "rating" );
157         PUSH_META( Date, "date" );
158         PUSH_META( Setting, "setting" );
159         PUSH_META( URL, "url" );
160         PUSH_META( Language, "language" );
161         PUSH_META( NowPlaying, "now_playing" );
162         PUSH_META( Publisher, "publisher" );
163         PUSH_META( EncodedBy, "encoded_by" );
164         PUSH_META( ArtworkURL, "artwork_url" );
165         PUSH_META( TrackID, "track_id" );
166         PUSH_META( TrackTotal, "track_total" );
167         PUSH_META( Director, "director" );
168         PUSH_META( Season, "season" );
169         PUSH_META( Episode, "episode" );
170         PUSH_META( ShowName, "show_name" );
171         PUSH_META( Actors, "actors" );
172 
173 #undef PUSH_META
174 
175         char **names = vlc_meta_CopyExtraNames(p_item->p_meta);
176         for(int i = 0; names[i]; i++)
177         {
178             const char *meta = vlc_meta_GetExtra(p_item->p_meta, names[i]);
179             lua_pushstring( L, meta );
180             lua_setfield( L, -2, names[i] );
181             free(names[i]);
182         }
183         free(names);
184     }
185     vlc_mutex_unlock(&p_item->lock);
186 
187     return 1;
188 }
189 
vlclua_input_item_stats(lua_State * L)190 static int vlclua_input_item_stats( lua_State *L )
191 {
192     input_item_t *p_item = vlclua_input_item_get_internal( L );
193     lua_newtable( L );
194     if( p_item == NULL )
195         return 1;
196 
197     vlc_mutex_lock( &p_item->lock );
198     input_stats_t *p_stats = p_item->p_stats;
199     if( p_stats != NULL )
200     {
201         vlc_mutex_lock( &p_item->p_stats->lock );
202 #define STATS_INT( n ) lua_pushinteger( L, p_item->p_stats->i_ ## n ); \
203                        lua_setfield( L, -2, #n );
204 #define STATS_FLOAT( n ) lua_pushnumber( L, p_item->p_stats->f_ ## n ); \
205                          lua_setfield( L, -2, #n );
206         STATS_INT( read_packets )
207         STATS_INT( read_bytes )
208         STATS_FLOAT( input_bitrate )
209         STATS_FLOAT( average_input_bitrate )
210         STATS_INT( demux_read_packets )
211         STATS_INT( demux_read_bytes )
212         STATS_FLOAT( demux_bitrate )
213         STATS_FLOAT( average_demux_bitrate )
214         STATS_INT( demux_corrupted )
215         STATS_INT( demux_discontinuity )
216         STATS_INT( decoded_audio )
217         STATS_INT( decoded_video )
218         STATS_INT( displayed_pictures )
219         STATS_INT( lost_pictures )
220         STATS_INT( sent_packets )
221         STATS_INT( sent_bytes )
222         STATS_FLOAT( send_bitrate )
223         STATS_INT( played_abuffers )
224         STATS_INT( lost_abuffers )
225 #undef STATS_INT
226 #undef STATS_FLOAT
227         vlc_mutex_unlock( &p_item->p_stats->lock );
228     }
229     vlc_mutex_unlock( &p_item->lock );
230     return 1;
231 }
232 
vlclua_input_add_subtitle(lua_State * L,bool b_path)233 static int vlclua_input_add_subtitle( lua_State *L, bool b_path )
234 {
235     input_thread_t *p_input = vlclua_get_input_internal( L );
236     bool b_autoselect = false;
237     if( !p_input )
238         return luaL_error( L, "can't add subtitle: no current input" );
239     if( !lua_isstring( L, 1 ) )
240     {
241         vlc_object_release( p_input );
242         return luaL_error( L, "vlc.input.add_subtitle() usage: (path)" );
243     }
244     if( lua_gettop( L ) >= 2 )
245         b_autoselect = lua_toboolean( L, 2 );
246     const char *psz_sub = luaL_checkstring( L, 1 );
247     if( !b_path )
248         input_AddSlave( p_input, SLAVE_TYPE_SPU, psz_sub, b_autoselect, true, false );
249     else
250     {
251         char* psz_mrl = vlc_path2uri( psz_sub, NULL );
252         if ( psz_mrl )
253         {
254             input_AddSlave( p_input, SLAVE_TYPE_SPU, psz_mrl, b_autoselect, true, false );
255             free( psz_mrl );
256         }
257     }
258     vlc_object_release( p_input );
259     return 1;
260 }
261 
vlclua_input_add_subtitle_path(lua_State * L)262 static int vlclua_input_add_subtitle_path( lua_State *L )
263 {
264     return vlclua_input_add_subtitle( L, true );
265 }
266 
vlclua_input_add_subtitle_mrl(lua_State * L)267 static int vlclua_input_add_subtitle_mrl( lua_State *L )
268 {
269     return vlclua_input_add_subtitle( L, false );
270 }
271 
272 /*****************************************************************************
273  * Input items
274  *****************************************************************************/
275 
vlclua_input_item_get_internal(lua_State * L)276 static input_item_t* vlclua_input_item_get_internal( lua_State *L )
277 {
278     input_item_t **pp_item = luaL_checkudata( L, 1, "input_item" );
279     input_item_t *p_item = *pp_item;
280 
281     if( !p_item )
282         luaL_error( L, "script went completely foobar" );
283 
284     return p_item;
285 }
286 
287 /* Garbage collection of an input_item_t */
vlclua_input_item_delete(lua_State * L)288 static int vlclua_input_item_delete( lua_State *L )
289 {
290     input_item_t **pp_item = luaL_checkudata( L, 1, "input_item" );
291     input_item_t *p_item = *pp_item;
292 
293     if( !p_item )
294         return luaL_error( L, "script went completely foobar" );
295 
296     *pp_item = NULL;
297     input_item_Release( p_item );
298 
299     return 1;
300 }
301 
vlclua_input_item_get_current(lua_State * L)302 static int vlclua_input_item_get_current( lua_State *L )
303 {
304     input_thread_t *p_input = vlclua_get_input_internal( L );
305     input_item_t *p_item = p_input ? input_GetItem( p_input ) : NULL;
306     if( !p_item )
307     {
308         lua_pushnil( L );
309         if( p_input ) vlc_object_release( p_input );
310         return 1;
311     }
312 
313     vlclua_input_item_get( L, p_item );
314 
315     if( p_input ) vlc_object_release( p_input );
316     return 1;
317 }
318 
vlclua_input_item_metas(lua_State * L)319 static int vlclua_input_item_metas( lua_State *L )
320 {
321     vlclua_input_metas_internal( L, vlclua_input_item_get_internal( L ) );
322     return 1;
323 }
324 
vlclua_input_item_is_preparsed(lua_State * L)325 static int vlclua_input_item_is_preparsed( lua_State *L )
326 {
327     lua_pushboolean( L, input_item_IsPreparsed( vlclua_input_item_get_internal( L ) ) );
328     return 1;
329 }
330 
vlclua_input_item_uri(lua_State * L)331 static int vlclua_input_item_uri( lua_State *L )
332 {
333     char *uri = input_item_GetURI( vlclua_input_item_get_internal( L ) );
334     lua_pushstring( L, uri );
335     free( uri );
336     return 1;
337 }
338 
vlclua_input_item_name(lua_State * L)339 static int vlclua_input_item_name( lua_State *L )
340 {
341     char *name = input_item_GetName( vlclua_input_item_get_internal( L ) );
342     lua_pushstring( L, name );
343     free( name );
344     return 1;
345 }
346 
vlclua_input_item_duration(lua_State * L)347 static int vlclua_input_item_duration( lua_State *L )
348 {
349     mtime_t duration = input_item_GetDuration( vlclua_input_item_get_internal( L ) );
350     lua_pushnumber( L, ((double)duration)/1000000. );
351     return 1;
352 }
353 
vlclua_input_item_set_meta(lua_State * L)354 static int vlclua_input_item_set_meta( lua_State *L )
355 {
356     input_item_t *p_item = vlclua_input_item_get_internal( L );
357     lua_settop( L, 1 + 2 ); // two arguments
358     const char *psz_name = luaL_checkstring( L, 2 ),
359                *psz_value = luaL_checkstring( L, 3 );
360 
361 #define META_TYPE( n, s ) { s, vlc_meta_ ## n },
362     static const struct
363     {
364         const char psz_name[15];
365         unsigned char type;
366     } pp_meta_types[] = {
367         META_TYPE( Title, "title" )
368         META_TYPE( Artist, "artist" )
369         META_TYPE( Genre, "genre" )
370         META_TYPE( Copyright, "copyright" )
371         META_TYPE( Album, "album" )
372         META_TYPE( TrackNumber, "track_number" )
373         META_TYPE( Description, "description" )
374         META_TYPE( Rating, "rating" )
375         META_TYPE( Date, "date" )
376         META_TYPE( Setting, "setting" )
377         META_TYPE( URL, "url" )
378         META_TYPE( Language, "language" )
379         META_TYPE( NowPlaying, "now_playing" )
380         META_TYPE( ESNowPlaying, "now_playing" )
381         META_TYPE( Publisher, "publisher" )
382         META_TYPE( EncodedBy, "encoded_by" )
383         META_TYPE( ArtworkURL, "artwork_url" )
384         META_TYPE( TrackID, "track_id" )
385         META_TYPE( TrackTotal, "track_total" )
386         META_TYPE( Director, "director" )
387         META_TYPE( Season, "season" )
388         META_TYPE( Episode, "episode" )
389         META_TYPE( ShowName, "show_name" )
390         META_TYPE( Actors, "actors" )
391         META_TYPE( AlbumArtist, "album_artist" )
392         META_TYPE( DiscNumber, "disc_number" )
393         META_TYPE( DiscTotal, "disc_total" )
394     };
395 #undef META_TYPE
396 
397     static_assert( sizeof(pp_meta_types)
398                       == VLC_META_TYPE_COUNT * sizeof(pp_meta_types[0]),
399                    "Inconsistent meta data types" );
400     vlc_meta_type_t type = vlc_meta_Title;
401     for( unsigned i = 0; i < VLC_META_TYPE_COUNT; i++ )
402     {
403         if( !strcasecmp( pp_meta_types[i].psz_name, psz_name ) )
404         {
405             type = pp_meta_types[i].type;
406             input_item_SetMeta( p_item, type, psz_value );
407             return 1;
408         }
409     }
410 
411     vlc_meta_AddExtra( p_item->p_meta, psz_name, psz_value );
412     return 1;
413 }
414 
415 /*****************************************************************************
416  * Lua bindings
417  *****************************************************************************/
418 static const luaL_Reg vlclua_input_reg[] = {
419     { "is_playing", vlclua_input_is_playing },
420     { "item", vlclua_input_item_get_current },
421     { "add_subtitle", vlclua_input_add_subtitle_path },
422     { "add_subtitle_mrl", vlclua_input_add_subtitle_mrl },
423     { NULL, NULL }
424 };
425 
luaopen_input(lua_State * L)426 void luaopen_input( lua_State *L )
427 {
428     lua_newtable( L );
429     luaL_register( L, NULL, vlclua_input_reg );
430     lua_setfield( L, -2, "input" );
431 }
432 
433 static const luaL_Reg vlclua_input_item_reg[] = {
434     { "is_preparsed", vlclua_input_item_is_preparsed },
435     { "metas", vlclua_input_item_metas },
436     { "set_meta", vlclua_input_item_set_meta },
437     { "uri", vlclua_input_item_uri },
438     { "name", vlclua_input_item_name },
439     { "duration", vlclua_input_item_duration },
440     { "stats", vlclua_input_item_stats },
441     { "info", vlclua_input_item_info },
442     { NULL, NULL }
443 };
444 
vlclua_input_item_get(lua_State * L,input_item_t * p_item)445 int vlclua_input_item_get( lua_State *L, input_item_t *p_item )
446 {
447     input_item_Hold( p_item );
448     input_item_t **pp = lua_newuserdata( L, sizeof( input_item_t* ) );
449     *pp = p_item;
450 
451     if( luaL_newmetatable( L, "input_item" ) )
452     {
453         lua_newtable( L );
454         luaL_register( L, NULL, vlclua_input_item_reg );
455         lua_setfield( L, -2, "__index" );
456         lua_pushcfunction( L, vlclua_input_item_delete );
457         lua_setfield( L, -2, "__gc" );
458     }
459 
460     lua_setmetatable(L, -2);
461 
462     return 1;
463 }
464 
465 
luaopen_input_item(lua_State * L,input_item_t * item)466 void luaopen_input_item( lua_State *L, input_item_t *item )
467 {
468     assert(item);
469     vlclua_input_item_get( L, item );
470     lua_setfield( L, -2, "item" );
471 }
472