static int vlclua_node_add_subitem( lua_State *L ) { services_discovery_t *p_sd = (services_discovery_t *)vlclua_get_this( L ); input_item_t **pp_node = (input_item_t **)luaL_checkudata( L, 1, "node" ); if( *pp_node ) { if( lua_istable( L, -1 ) ) { lua_getfield( L, -1, "path" ); if( lua_isstring( L, -1 ) ) { const char *psz_path = lua_tostring( L, -1 ); /* The table must be at the top of the stack when calling * vlclua_read_options() */ char **ppsz_options = NULL; int i_options = 0; lua_pushvalue( L, -2 ); vlclua_read_options( p_sd, L, &i_options, &ppsz_options ); input_item_t *p_input = input_item_NewExt( psz_path, psz_path, i_options, (const char **)ppsz_options, VLC_INPUT_OPTION_TRUSTED, -1 ); lua_pop( L, 2 ); if( p_input ) { input_item_node_t *p_input_node = input_item_node_Create( *pp_node ); vlclua_read_meta_data( p_sd, L, p_input ); /* This one is to be tested... */ vlclua_read_custom_meta_data( p_sd, L, p_input ); lua_getfield( L, -1, "duration" ); if( lua_isnumber( L, -1 ) ) input_item_SetDuration( p_input, (lua_tonumber( L, -1 )*1e6) ); else if( !lua_isnil( L, -1 ) ) msg_Warn( p_sd, "Item duration should be a number (in seconds)." ); lua_pop( L, 1 ); input_item_node_AppendItem( p_input_node, p_input ); input_item_node_PostAndDelete( p_input_node ); input_item_t **udata = (input_item_t **) lua_newuserdata( L, sizeof( input_item_t * ) ); *udata = p_input; if( luaL_newmetatable( L, "input_item_t" ) ) { lua_newtable( L ); luaL_register( L, NULL, vlclua_item_reg ); lua_setfield( L, -2, "__index" ); lua_pushliteral( L, "none of your business" ); lua_setfield( L, -2, "__metatable" ); } lua_setmetatable( L, -2 ); vlc_gc_decref( p_input ); } while( i_options > 0 ) free( ppsz_options[--i_options] ); free( ppsz_options ); } else msg_Err( p_sd, "node:add_subitem: the \"path\" parameter can't be empty" ); } else msg_Err( p_sd, "Error parsing add_subitem arguments" ); } return 1; }
/** * Main demux callback function * @param p_demux: this demux object */ static int Demux( demux_t *p_demux ) { char *psz_line; char *psz_artist = NULL, *psz_album = NULL, *psz_genre = NULL, *psz_year = NULL; char *psz_author = NULL, *psz_title = NULL, *psz_copyright = NULL, *psz_cdnum = NULL, *psz_comments = NULL; int i_parsed_duration = 0; mtime_t i_duration = -1; const char **ppsz_options = NULL; int i_options = 0, i_start = 0, i_stop = 0; bool b_cleanup = false; input_item_t *p_input; input_item_t *p_current_input = GetCurrentItem(p_demux); input_item_node_t *p_subitems = input_item_node_Create( p_current_input ); psz_line = stream_ReadLine( p_demux->s ); while( psz_line ) { char *psz_parse = psz_line; /* Skip leading tabs and spaces */ while( *psz_parse == ' ' || *psz_parse == '\t' || *psz_parse == '\n' || *psz_parse == '\r' ) psz_parse++; if( *psz_parse == '#' ) { /* Ignore comments */ } else if( *psz_parse ) { char *psz_mrl, *psz_option_next, *psz_option; char *psz_param, *psz_value; /* Get the MRL from the file. Note that this might contain parameters of form ?param1=value1¶m2=value2 in a RAM file */ psz_mrl = ProcessMRL( psz_parse, p_demux->p_sys->psz_prefix ); b_cleanup = true; if ( !psz_mrl ) goto error; /* We have the MRL, now we have to check for options and parse them from MRL */ psz_option = strchr( psz_mrl, '?' ); /* Look for start of options */ if( psz_option ) { /* Remove options from MRL because VLC can't get the file otherwise */ *psz_option = '\0'; psz_option++; psz_option_next = psz_option; while( 1 ) /* Process each option */ { /* Look for end of first option which maybe a & or \0 */ psz_option = psz_option_next; psz_option_next = strchr( psz_option, '&' ); if( psz_option_next ) { *psz_option_next = '\0'; psz_option_next++; } else psz_option_next = strchr( psz_option, '\0' ); /* Quit if options are over */ if( psz_option_next == psz_option ) break; /* Parse out param and value */ psz_param = psz_option; psz_value = strchr( psz_option, '=' ); if( psz_value == NULL ) break; *psz_value = '\0'; psz_value++; /* Take action based on parameter value in the below if else structure */ /* TODO: Remove any quotes surrounding values if required */ if( !strcmp( psz_param, "clipinfo" ) ) { ParseClipInfo( psz_value, &psz_artist, &psz_title, &psz_album, &psz_genre, &psz_year, &psz_cdnum, &psz_comments ); /* clipinfo has various sub parameters, which is parsed by this function */ } else if( !strcmp( psz_param, "author" ) ) { psz_author = decode_URI_duplicate(psz_value); EnsureUTF8( psz_author ); } else if( !strcmp( psz_param, "start" ) && strncmp( psz_mrl, "rtsp", 4 ) /* Our rtsp-real or our real demuxer is wrong */ ) { i_start = ParseTime( psz_value, strlen( psz_value ) ); char *temp; if( i_start ) { if( asprintf( &temp, ":start-time=%d", i_start ) != -1 ) INSERT_ELEM( ppsz_options, i_options, i_options, temp ); } } else if( !strcmp( psz_param, "end" ) ) { i_stop = ParseTime( psz_value, strlen( psz_value ) ); char *temp; if( i_stop ) { if( asprintf( &temp, ":stop-time=%d", i_stop ) != -1 ) INSERT_ELEM( ppsz_options, i_options, i_options, temp ); } } else if( !strcmp( psz_param, "title" ) ) { psz_title = decode_URI_duplicate(psz_value); EnsureUTF8( psz_title ); } else if( !strcmp( psz_param, "copyright" ) ) { psz_copyright = decode_URI_duplicate(psz_value); EnsureUTF8( psz_copyright ); } else { /* TODO: insert option anyway? Currently ignores*/ /* INSERT_ELEM( ppsz_options, i_options, i_options, psz_option ); */ } } } /* Create the input item and pump in all the options into playlist item */ p_input = input_item_NewExt( psz_mrl, psz_title, i_options, ppsz_options, 0, i_duration ); if( !EMPTY_STR( psz_artist ) ) input_item_SetArtist( p_input, psz_artist ); if( !EMPTY_STR( psz_author ) ) input_item_SetPublisher( p_input, psz_author ); if( !EMPTY_STR( psz_title ) ) input_item_SetTitle( p_input, psz_title ); if( !EMPTY_STR( psz_copyright ) ) input_item_SetCopyright( p_input, psz_copyright ); if( !EMPTY_STR( psz_album ) ) input_item_SetAlbum( p_input, psz_album ); if( !EMPTY_STR( psz_genre ) ) input_item_SetGenre( p_input, psz_genre ); if( !EMPTY_STR( psz_year ) ) input_item_SetDate( p_input, psz_copyright ); if( !EMPTY_STR( psz_cdnum ) ) input_item_SetTrackNum( p_input, psz_cdnum ); if( !EMPTY_STR( psz_comments ) ) input_item_SetDescription( p_input, psz_comments ); input_item_node_AppendItem( p_subitems, p_input ); vlc_gc_decref( p_input ); free( psz_mrl ); } error: /* Fetch another line */ free( psz_line ); psz_line = stream_ReadLine( p_demux->s ); if( !psz_line ) b_cleanup = true; if( b_cleanup ) { /* Cleanup state */ while( i_options-- ) free( (char*)ppsz_options[i_options] ); FREENULL( ppsz_options ); FREENULL( psz_artist ); FREENULL( psz_title ); FREENULL( psz_author ); FREENULL( psz_copyright ); FREENULL( psz_album ); FREENULL( psz_genre ); FREENULL( psz_year ); FREENULL( psz_cdnum ); FREENULL( psz_comments ); i_options = 0; i_parsed_duration = 0; i_duration = -1; i_start = 0; i_stop = 0; b_cleanup = false; } } input_item_node_PostAndDelete( p_subitems ); vlc_gc_decref(p_current_input); var_Destroy( p_demux, "m3u-extvlcopt" ); return 0; /* Needed for correct operation of go back */ }
static int Demux( demux_t *p_demux ) { char *psz_name = NULL; char *psz_line; char *psz_mrl = NULL; char *psz_mrl_orig = NULL; char *psz_key; char *psz_value; int i_item = -1; input_item_t *p_input; input_item_t *p_current_input = GetCurrentItem(p_demux); input_item_node_t *p_subitems = input_item_node_Create( p_current_input ); while( ( psz_line = stream_ReadLine( p_demux->s ) ) ) { if( !strncasecmp( psz_line, "[playlist]", sizeof("[playlist]")-1 ) || !strncasecmp( psz_line, "[Reference]", sizeof("[Reference]")-1 ) ) { free( psz_line ); continue; } psz_key = psz_line; psz_value = strchr( psz_line, '=' ); if( psz_value ) { *psz_value='\0'; psz_value++; } else { free( psz_line ); continue; } if( !strcasecmp( psz_key, "version" ) ) { msg_Dbg( p_demux, "pls file version: %s", psz_value ); free( psz_line ); continue; } if( !strcasecmp( psz_key, "numberofentries" ) ) { msg_Dbg( p_demux, "pls should have %d entries", atoi(psz_value) ); free( psz_line); continue; } /* find the number part of of file1, title1 or length1 etc */ int i_new_item; if( sscanf( psz_key, "%*[^0-9]%d", &i_new_item ) != 1 ) { msg_Warn( p_demux, "couldn't find number of items" ); free( psz_line ); continue; } if( i_item == -1 ) i_item = i_new_item; else if( i_item != i_new_item ) { /* we found a new item, insert the previous */ if( psz_mrl ) { p_input = input_item_New( psz_mrl, psz_name ); input_item_CopyOptions( p_current_input, p_input ); input_item_node_AppendItem( p_subitems, p_input ); vlc_gc_decref( p_input ); free( psz_mrl_orig ); psz_mrl_orig = psz_mrl = NULL; } else { msg_Warn( p_demux, "no file= part found for item %d", i_item ); } free( psz_name ); psz_name = NULL; i_item = i_new_item; } if( !strncasecmp( psz_key, "file", sizeof("file") -1 ) || !strncasecmp( psz_key, "Ref", sizeof("Ref") -1 ) ) { free( psz_mrl_orig ); psz_mrl_orig = psz_mrl = ProcessMRL( psz_value, p_demux->p_sys->psz_prefix ); if( !strncasecmp( psz_key, "Ref", sizeof("Ref") -1 ) ) { if( !strncasecmp( psz_mrl, "http://", sizeof("http://") -1 ) ) memcpy( psz_mrl, "mmsh", 4 ); } } else if( !strncasecmp( psz_key, "title", sizeof("title") -1 ) ) { free( psz_name ); psz_name = strdup( psz_value ); } else if( !strncasecmp( psz_key, "length", sizeof("length") -1 ) ) /* duration in seconds */; else { msg_Warn( p_demux, "unknown key found in pls file: %s", psz_key ); } free( psz_line ); } /* Add last object */ if( psz_mrl ) { p_input = input_item_New( psz_mrl, psz_name ); input_item_CopyOptions( p_current_input, p_input ); input_item_node_AppendItem( p_subitems, p_input ); vlc_gc_decref( p_input ); free( psz_mrl_orig ); } else { msg_Warn( p_demux, "no file= part found for item %d", i_item ); } free( psz_name ); psz_name = NULL; input_item_node_PostAndDelete( p_subitems ); vlc_gc_decref(p_current_input); return 0; /* Needed for correct operation of go back */ }
/* This won't hold the item, but can tell to interested third parties * Like the playlist, that there is a new sub item. With this design * It is not the input item's responsability to keep all the ref of * the input item children. */ void input_item_PostSubItem( 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_node_PostAndDelete( p_node ); }
int vlclua_playlist_add_internal( vlc_object_t *p_this, lua_State *L, playlist_t *p_playlist, input_item_t *p_parent, bool b_play ) { int i_count = 0; input_item_node_t *p_parent_node = NULL; assert( p_parent || p_playlist ); /* playlist */ if( lua_istable( L, -1 ) ) { if( p_parent ) p_parent_node = input_item_node_Create( p_parent ); lua_pushnil( L ); /* playlist nil */ while( lua_next( L, -2 ) ) { /* playlist key item */ /* <Parse playlist item> */ if( lua_istable( L, -1 ) ) { lua_getfield( L, -1, "path" ); /* playlist key item path */ if( lua_isstring( L, -1 ) ) { char *psz_oldurl = NULL; const char *psz_path = NULL; const char *psz_name = NULL; char **ppsz_options = NULL; int i_options = 0; mtime_t i_duration = -1; input_item_t *p_input; /* Read path and name */ if (p_parent) { psz_oldurl = input_item_GetURI( p_parent ); msg_Dbg( p_this, "old path: %s", psz_oldurl ); } psz_path = lua_tostring( L, -1 ); msg_Dbg( p_this, "Path: %s", psz_path ); lua_getfield( L, -2, "name" ); /* playlist key item path name */ if( lua_isstring( L, -1 ) ) { psz_name = lua_tostring( L, -1 ); msg_Dbg( p_this, "Name: %s", psz_name ); } else { if( !lua_isnil( L, -1 ) ) msg_Warn( p_this, "Playlist item name should be a string." ); } /* Read duration */ lua_getfield( L, -3, "duration" ); /* playlist key item path name duration */ if( lua_isnumber( L, -1 ) ) { i_duration = (mtime_t)(lua_tonumber( L, -1 )*1e6); } else if( !lua_isnil( L, -1 ) ) { msg_Warn( p_this, "Playlist item duration should be a number (in seconds)." ); } lua_pop( L, 1 ); /* pop "duration" */ /* playlist key item path name */ /* Read options: item must be on top of stack */ lua_pushvalue( L, -3 ); /* playlist key item path name item */ vlclua_read_options( p_this, L, &i_options, &ppsz_options ); /* Create input item */ p_input = input_item_NewExt( psz_path, psz_name, i_duration, ITEM_TYPE_UNKNOWN, ITEM_NET_UNKNOWN ); input_item_AddOptions( p_input, i_options, (const char **)ppsz_options, VLC_INPUT_OPTION_TRUSTED ); lua_pop( L, 3 ); /* pop "path name item" */ /* playlist key item */ /* Read meta data: item must be on top of stack */ vlclua_read_meta_data( p_this, L, p_input ); /* copy the original URL to the meta data, if "URL" is still empty */ char* url = input_item_GetURL( p_input ); if( url == NULL && p_parent) { EnsureUTF8( psz_oldurl ); msg_Dbg( p_this, "meta-URL: %s", psz_oldurl ); input_item_SetURL ( p_input, psz_oldurl ); } free( psz_oldurl ); free( url ); /* copy the psz_name to the meta data, if "Title" is still empty */ char* title = input_item_GetTitle( p_input ); if( title == NULL ) input_item_SetTitle ( p_input, psz_name ); free( title ); /* Read custom meta data: item must be on top of stack*/ vlclua_read_custom_meta_data( p_this, L, p_input ); /* Append item to playlist */ if( p_parent ) /* Add to node */ { input_item_CopyOptions( p_input, p_parent ); input_item_node_AppendItem( p_parent_node, p_input ); } else /* Play or Enqueue (preparse) */ /* FIXME: playlist_AddInput() can fail */ playlist_AddInput( p_playlist, p_input, ( b_play ? PLAYLIST_GO : 0 ), true ); i_count ++; /* increment counter */ vlc_gc_decref( p_input ); while( i_options > 0 ) free( ppsz_options[--i_options] ); free( ppsz_options ); } else { lua_pop( L, 1 ); /* pop "path" */ msg_Warn( p_this, "Playlist item's path should be a string" ); } /* playlist key item */ } else { msg_Warn( p_this, "Playlist item should be a table" ); } /* <Parse playlist item> */ lua_pop( L, 1 ); /* pop the value, keep the key for * the next lua_next() call */ /* playlist key */ } /* playlist */ if( p_parent ) { if( i_count ) input_item_node_PostAndDelete( p_parent_node ); else input_item_node_Delete( p_parent_node ); } } else { msg_Warn( p_this, "Playlist should be a table." ); } return i_count; }
/** * \brief demuxer function for XSPF parsing */ int Demux( demux_t *p_demux ) { int i_ret = -1; xml_t *p_xml = NULL; xml_reader_t *p_xml_reader = NULL; char *psz_name = NULL; input_item_t *p_current_input = GetCurrentItem(p_demux); p_demux->p_sys->pp_tracklist = NULL; p_demux->p_sys->i_tracklist_entries = 0; p_demux->p_sys->i_track_id = -1; p_demux->p_sys->psz_base = NULL; /* create new xml parser from stream */ p_xml = xml_Create( p_demux ); if( !p_xml ) goto end; p_xml_reader = xml_ReaderCreate( p_xml, p_demux->s ); if( !p_xml_reader ) goto end; /* locating the root node */ do { if( xml_ReaderRead( p_xml_reader ) != 1 ) { msg_Err( p_demux, "can't read xml stream" ); goto end; } } while( xml_ReaderNodeType( p_xml_reader ) != XML_READER_STARTELEM ); /* checking root node name */ psz_name = xml_ReaderName( p_xml_reader ); if( !psz_name || strcmp( psz_name, "playlist" ) ) { msg_Err( p_demux, "invalid root node name: %s", psz_name ); free( psz_name ); goto end; } free( psz_name ); input_item_node_t *p_subitems = input_item_node_Create( p_current_input ); i_ret = parse_playlist_node( p_demux, p_subitems, p_xml_reader, "playlist" ) ? 0 : -1; for( int i = 0 ; i < p_demux->p_sys->i_tracklist_entries ; i++ ) { input_item_t *p_new_input = p_demux->p_sys->pp_tracklist[i]; if( p_new_input ) { input_item_node_AppendItem( p_subitems, p_new_input ); } } input_item_node_PostAndDelete( p_subitems ); end: vlc_gc_decref(p_current_input); if( p_xml_reader ) xml_ReaderDelete( p_xml, p_xml_reader ); if( p_xml ) xml_Delete( p_xml ); return i_ret; /* Needed for correct operation of go back */ }
/* "specs" : http://phobos.apple.com/static/iTunesRSS.html */ static int Demux( demux_t *p_demux ) { bool b_item = false; bool b_image = false; xml_reader_t *p_xml_reader; char *psz_elname = NULL; char *psz_item_mrl = NULL; char *psz_item_size = NULL; char *psz_item_type = NULL; char *psz_item_name = NULL; char *psz_item_date = NULL; char *psz_item_author = NULL; char *psz_item_category = NULL; char *psz_item_duration = NULL; char *psz_item_keywords = NULL; char *psz_item_subtitle = NULL; char *psz_item_summary = NULL; char *psz_art_url = NULL; const char *node; int i_type; input_item_t *p_input; input_item_node_t *p_subitems = NULL; input_item_t *p_current_input = GetCurrentItem(p_demux); p_xml_reader = xml_ReaderCreate( p_demux, p_demux->s ); if( !p_xml_reader ) goto error; /* xml */ /* check root node */ if( xml_ReaderNextNode( p_xml_reader, &node ) != XML_READER_STARTELEM ) { msg_Err( p_demux, "invalid file (no root node)" ); goto error; } if( strcmp( node, "rss" ) ) { msg_Err( p_demux, "invalid root node <%s>", node ); goto error; } p_subitems = input_item_node_Create( p_current_input ); while( (i_type = xml_ReaderNextNode( p_xml_reader, &node )) > 0 ) { switch( i_type ) { case XML_READER_STARTELEM: { free( psz_elname ); psz_elname = strdup( node ); if( unlikely(!node) ) goto error; if( !strcmp( node, "item" ) ) b_item = true; else if( !strcmp( node, "image" ) ) b_image = true; // Read the attributes const char *attr, *value; while( (attr = xml_ReaderNextAttr( p_xml_reader, &value )) ) { if( !strcmp( node, "enclosure" ) ) { char **p = NULL; if( !strcmp( attr, "url" ) ) p = &psz_item_mrl; else if( !strcmp( attr, "length" ) ) p = &psz_item_size; else if( !strcmp( attr, "type" ) ) p = &psz_item_type; if( p != NULL ) { free( *p ); *p = strdup( value ); } else msg_Dbg( p_demux,"unhandled attribute %s in <%s>", attr, node ); } else msg_Dbg( p_demux,"unhandled attribute %s in <%s>", attr, node ); } break; } case XML_READER_TEXT: { if(!psz_elname) break; /* item specific meta data */ if( b_item ) { char **p; if( !strcmp( psz_elname, "title" ) ) p = &psz_item_name; else if( !strcmp( psz_elname, "itunes:author" ) || !strcmp( psz_elname, "author" ) ) /* <author> isn't standard iTunes podcast stuff */ p = &psz_item_author; else if( !strcmp( psz_elname, "itunes:summary" ) || !strcmp( psz_elname, "description" ) ) /* <description> isn't standard iTunes podcast stuff */ p = &psz_item_summary; else if( !strcmp( psz_elname, "pubDate" ) ) p = &psz_item_date; else if( !strcmp( psz_elname, "itunes:category" ) ) p = &psz_item_category; else if( !strcmp( psz_elname, "itunes:duration" ) ) p = &psz_item_duration; else if( !strcmp( psz_elname, "itunes:keywords" ) ) p = &psz_item_keywords; else if( !strcmp( psz_elname, "itunes:subtitle" ) ) p = &psz_item_subtitle; else break; free( *p ); *p = strdup( node ); } /* toplevel meta data */ else if( !b_image ) { if( !strcmp( psz_elname, "title" ) ) input_item_SetName( p_current_input, node ); #define ADD_GINFO( info, name ) \ else if( !strcmp( psz_elname, name ) ) \ input_item_AddInfo( p_current_input, _("Podcast Info"), \ info, "%s", node ); ADD_GINFO( _("Podcast Link"), "link" ) ADD_GINFO( _("Podcast Copyright"), "copyright" ) ADD_GINFO( _("Podcast Category"), "itunes:category" ) ADD_GINFO( _("Podcast Keywords"), "itunes:keywords" ) ADD_GINFO( _("Podcast Subtitle"), "itunes:subtitle" ) #undef ADD_GINFO else if( !strcmp( psz_elname, "itunes:summary" ) || !strcmp( psz_elname, "description" ) ) { /* <description> isn't standard iTunes podcast stuff */ input_item_AddInfo( p_current_input, _( "Podcast Info" ), _( "Podcast Summary" ), "%s", node ); } } else { if( !strcmp( psz_elname, "url" ) ) { free( psz_art_url ); psz_art_url = strdup( node ); } else msg_Dbg( p_demux, "unhandled text in element <%s>", psz_elname ); } break; } // End element case XML_READER_ENDELEM: { FREENULL( psz_elname ); if( !strcmp( node, "item" ) ) { if( psz_item_mrl == NULL ) { msg_Err( p_demux, "invalid XML (no enclosure markup)" ); goto error; } p_input = input_item_New( psz_item_mrl, psz_item_name ); FREENULL( psz_item_mrl ); FREENULL( psz_item_name ); if( p_input == NULL ) break; /* FIXME: meta data memory leaks? */ /* Set the duration if available */ if( psz_item_duration ) input_item_SetDuration( p_input, strTimeToMTime( psz_item_duration ) ); #define ADD_INFO( info, field ) \ if( field ) { \ input_item_AddInfo( p_input, _( "Podcast Info" ), (info), "%s", \ (field) ); \ FREENULL( field ); } ADD_INFO( _("Podcast Publication Date"), psz_item_date ); ADD_INFO( _("Podcast Author"), psz_item_author ); ADD_INFO( _("Podcast Subcategory"), psz_item_category ); ADD_INFO( _("Podcast Duration"), psz_item_duration ); ADD_INFO( _("Podcast Keywords"), psz_item_keywords ); ADD_INFO( _("Podcast Subtitle"), psz_item_subtitle ); ADD_INFO( _("Podcast Summary"), psz_item_summary ); ADD_INFO( _("Podcast Type"), psz_item_type ); #undef ADD_INFO /* Add the global art url to this item, if any */ if( psz_art_url ) input_item_SetArtURL( p_input, psz_art_url ); if( psz_item_size ) { input_item_AddInfo( p_input, _( "Podcast Info" ), _( "Podcast Size" ), _("%s bytes"), psz_item_size ); FREENULL( psz_item_size ); } input_item_node_AppendItem( p_subitems, p_input ); vlc_gc_decref( p_input ); b_item = false; } else if( !strcmp( node, "image" ) ) { b_image = false; } break; } } }