LGPL
[vlc.git] / src / input / item.c
1 /*****************************************************************************
2  * item.c: input_item management
3  *****************************************************************************
4  * Copyright (C) 1998-2004 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: ClĂ©ment Stenac <zorglub@videolan.org>
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27 #include <assert.h>
28 #include <time.h>
29
30 #include <vlc_common.h>
31 #include <vlc_url.h>
32 #include "vlc_playlist.h"
33 #include "vlc_interface.h"
34 #include <vlc_charset.h>
35 #include <vlc_atomic.h>
36
37 #include "item.h"
38 #include "info.h"
39
40 static int GuessType( const input_item_t *p_item );
41
42 static inline void input_item_Clean( input_item_t *p_i )
43 {
44     int i;
45
46     vlc_event_manager_fini( &p_i->event_manager );
47
48     free( p_i->psz_name );
49     free( p_i->psz_uri );
50     if( p_i->p_stats )
51     {
52         vlc_mutex_destroy( &p_i->p_stats->lock );
53         free( p_i->p_stats );
54     }
55
56     if( p_i->p_meta )
57         vlc_meta_Delete( p_i->p_meta );
58
59     for( i = 0; i < p_i->i_options; i++ )
60         free( p_i->ppsz_options[i] );
61     TAB_CLEAN( p_i->i_options, p_i->ppsz_options );
62     free( p_i->optflagv);
63
64     for( i = 0; i < p_i->i_es; i++ )
65     {
66         es_format_Clean( p_i->es[i] );
67         free( p_i->es[i] );
68     }
69     TAB_CLEAN( p_i->i_es, p_i->es );
70
71     for( i = 0; i < p_i->i_epg; i++ )
72         vlc_epg_Delete( p_i->pp_epg[i] );
73     TAB_CLEAN( p_i->i_epg, p_i->pp_epg );
74
75     for( i = 0; i < p_i->i_categories; i++ )
76         info_category_Delete( p_i->pp_categories[i] );
77     TAB_CLEAN( p_i->i_categories, p_i->pp_categories );
78
79     vlc_mutex_destroy( &p_i->lock );
80 }
81 void input_item_SetErrorWhenReading( input_item_t *p_i, bool b_error )
82 {
83     bool b_changed;
84
85     vlc_mutex_lock( &p_i->lock );
86
87     b_changed = p_i->b_error_when_reading != b_error;
88     p_i->b_error_when_reading = b_error;
89
90     vlc_mutex_unlock( &p_i->lock );
91
92     if( b_changed )
93     {
94         vlc_event_t event;
95
96         event.type = vlc_InputItemErrorWhenReadingChanged;
97         event.u.input_item_error_when_reading_changed.new_value = b_error;
98         vlc_event_send( &p_i->event_manager, &event );
99     }
100 }
101 void input_item_SetPreparsed( input_item_t *p_i, bool b_preparsed )
102 {
103     bool b_send_event = false;
104
105     vlc_mutex_lock( &p_i->lock );
106
107     if( !p_i->p_meta )
108         p_i->p_meta = vlc_meta_New();
109
110     int status = vlc_meta_GetStatus(p_i->p_meta);
111     int new_status;
112     if( b_preparsed )
113         new_status = status | ITEM_PREPARSED;
114     else
115         new_status = status & ~ITEM_PREPARSED;
116     if( status != new_status )
117     {
118         vlc_meta_SetStatus(p_i->p_meta, new_status);
119         b_send_event = true;
120     }
121
122     vlc_mutex_unlock( &p_i->lock );
123
124     if( b_send_event )
125     {
126         vlc_event_t event;
127         event.type = vlc_InputItemPreparsedChanged;
128         event.u.input_item_preparsed_changed.new_status = new_status;
129         vlc_event_send( &p_i->event_manager, &event );
130     }
131 }
132
133 void input_item_SetArtNotFound( input_item_t *p_i, bool b_not_found )
134 {
135     vlc_mutex_lock( &p_i->lock );
136
137     if( !p_i->p_meta )
138         p_i->p_meta = vlc_meta_New();
139
140     int status = vlc_meta_GetStatus(p_i->p_meta);
141
142     if( b_not_found )
143         status |= ITEM_ART_NOTFOUND;
144     else
145         status &= ~ITEM_ART_NOTFOUND;
146
147     vlc_meta_SetStatus(p_i->p_meta, status);
148
149     vlc_mutex_unlock( &p_i->lock );
150 }
151
152 void input_item_SetArtFetched( input_item_t *p_i, bool b_art_fetched )
153 {
154     vlc_mutex_lock( &p_i->lock );
155
156     if( !p_i->p_meta )
157         p_i->p_meta = vlc_meta_New();
158
159     int status = vlc_meta_GetStatus(p_i->p_meta);
160
161     if( b_art_fetched )
162         status |= ITEM_ART_FETCHED;
163     else
164         status &= ~ITEM_ART_FETCHED;
165
166     vlc_meta_SetStatus(p_i->p_meta, status);
167
168     vlc_mutex_unlock( &p_i->lock );
169 }
170
171 void input_item_SetMeta( input_item_t *p_i, vlc_meta_type_t meta_type, const char *psz_val )
172 {
173     vlc_event_t event;
174
175     vlc_mutex_lock( &p_i->lock );
176     if( !p_i->p_meta )
177         p_i->p_meta = vlc_meta_New();
178     vlc_meta_Set( p_i->p_meta, meta_type, psz_val );
179     vlc_mutex_unlock( &p_i->lock );
180
181     /* Notify interested third parties */
182     event.type = vlc_InputItemMetaChanged;
183     event.u.input_item_meta_changed.meta_type = meta_type;
184     vlc_event_send( &p_i->event_manager, &event );
185 }
186
187 /* FIXME GRRRRRRRRRR args should be in the reverse order to be
188  * consistent with (nearly?) all or copy funcs */
189 void input_item_CopyOptions( input_item_t *p_parent,
190                              input_item_t *p_child )
191 {
192     vlc_mutex_lock( &p_parent->lock );
193
194     for( int i = 0 ; i< p_parent->i_options; i++ )
195     {
196         if( !strcmp( p_parent->ppsz_options[i], "meta-file" ) )
197             continue;
198
199         input_item_AddOption( p_child,
200                               p_parent->ppsz_options[i],
201                               p_parent->optflagv[i] );
202     }
203
204     vlc_mutex_unlock( &p_parent->lock );
205 }
206
207 static void post_subitems( input_item_node_t *p_node )
208 {
209     for( int i = 0; i < p_node->i_children; i++ )
210     {
211         vlc_event_t event;
212         event.type = vlc_InputItemSubItemAdded;
213         event.u.input_item_subitem_added.p_new_child = p_node->pp_children[i]->p_item;
214         vlc_event_send( &p_node->p_item->event_manager, &event );
215
216         post_subitems( p_node->pp_children[i] );
217     }
218 }
219
220 /* This won't hold the item, but can tell to interested third parties
221  * Like the playlist, that there is a new sub item. With this design
222  * It is not the input item's responsability to keep all the ref of
223  * the input item children. */
224 void input_item_PostSubItem( input_item_t *p_parent, input_item_t *p_child )
225 {
226     input_item_node_t *p_node = input_item_node_Create( p_parent );
227     input_item_node_AppendItem( p_node, p_child );
228     input_item_node_PostAndDelete( p_node );
229 }
230
231 bool input_item_HasErrorWhenReading( input_item_t *p_item )
232 {
233     vlc_mutex_lock( &p_item->lock );
234
235     bool b_error = p_item->b_error_when_reading;
236
237     vlc_mutex_unlock( &p_item->lock );
238
239     return b_error;
240 }
241
242 bool input_item_MetaMatch( input_item_t *p_i,
243                            vlc_meta_type_t meta_type, const char *psz )
244 {
245     vlc_mutex_lock( &p_i->lock );
246
247     if( !p_i->p_meta )
248     {
249         vlc_mutex_unlock( &p_i->lock );
250         return false;
251     }
252     const char *psz_meta = vlc_meta_Get( p_i->p_meta, meta_type );
253     bool b_ret = psz_meta && strcasestr( psz_meta, psz );
254
255     vlc_mutex_unlock( &p_i->lock );
256
257     return b_ret;
258 }
259
260 char *input_item_GetMeta( input_item_t *p_i, vlc_meta_type_t meta_type )
261 {
262     vlc_mutex_lock( &p_i->lock );
263
264     if( !p_i->p_meta )
265     {
266         vlc_mutex_unlock( &p_i->lock );
267         return NULL;
268     }
269
270     char *psz = NULL;
271     if( vlc_meta_Get( p_i->p_meta, meta_type ) )
272         psz = strdup( vlc_meta_Get( p_i->p_meta, meta_type ) );
273
274     vlc_mutex_unlock( &p_i->lock );
275     return psz;
276 }
277
278 /* Get the title of a given item or fallback to the name if the title is empty */
279 char *input_item_GetTitleFbName( input_item_t *p_item )
280 {
281     char *psz_ret;
282     vlc_mutex_lock( &p_item->lock );
283
284     if( !p_item->p_meta )
285     {
286         psz_ret = p_item->psz_name ? strdup( p_item->psz_name ) : NULL;
287         vlc_mutex_unlock( &p_item->lock );
288         return psz_ret;
289     }
290
291     const char *psz_title = vlc_meta_Get( p_item->p_meta, vlc_meta_Title );
292     if( !EMPTY_STR( psz_title ) )
293         psz_ret = strdup( psz_title );
294     else
295         psz_ret = p_item->psz_name ? strdup( p_item->psz_name ) : NULL;
296
297     vlc_mutex_unlock( &p_item->lock );
298     return psz_ret;
299 }
300
301 char *input_item_GetName( input_item_t *p_item )
302 {
303     vlc_mutex_lock( &p_item->lock );
304
305     char *psz_name = p_item->psz_name ? strdup( p_item->psz_name ) : NULL;
306
307     vlc_mutex_unlock( &p_item->lock );
308     return psz_name;
309 }
310 void input_item_SetName( input_item_t *p_item, const char *psz_name )
311 {
312     vlc_mutex_lock( &p_item->lock );
313
314     free( p_item->psz_name );
315     p_item->psz_name = strdup( psz_name );
316
317     vlc_mutex_unlock( &p_item->lock );
318 }
319
320 char *input_item_GetURI( input_item_t *p_i )
321 {
322     vlc_mutex_lock( &p_i->lock );
323
324     char *psz_s = p_i->psz_uri ? strdup( p_i->psz_uri ) : NULL;
325
326     vlc_mutex_unlock( &p_i->lock );
327     return psz_s;
328 }
329
330 void input_item_SetURI( input_item_t *p_i, const char *psz_uri )
331 {
332     assert( psz_uri );
333 #ifndef NDEBUG
334     if( !strstr( psz_uri, "://" )
335      || strchr( psz_uri, ' ' ) || strchr( psz_uri, '"' ) )
336         fprintf( stderr, "Warning: %s(\"%s\"): file path instead of URL.\n",
337                  __func__, psz_uri );
338 #endif
339     vlc_mutex_lock( &p_i->lock );
340     free( p_i->psz_uri );
341     p_i->psz_uri = strdup( psz_uri );
342
343     p_i->i_type = GuessType( p_i );
344
345     if( p_i->psz_name )
346         ;
347     else
348     if( p_i->i_type == ITEM_TYPE_FILE || p_i->i_type == ITEM_TYPE_DIRECTORY )
349     {
350         const char *psz_filename = strrchr( p_i->psz_uri, '/' );
351
352         if( psz_filename && *psz_filename == '/' )
353             psz_filename++;
354         if( psz_filename && *psz_filename )
355             p_i->psz_name = strdup( psz_filename );
356
357         /* Make the name more readable */
358         if( p_i->psz_name )
359         {
360             decode_URI( p_i->psz_name );
361             EnsureUTF8( p_i->psz_name );
362         }
363     }
364     else
365     {   /* Strip login and password from title */
366         int r;
367         vlc_url_t url;
368
369         vlc_UrlParse( &url, psz_uri, 0 );
370         if( url.psz_protocol )
371         {
372             if( url.i_port > 0 )
373                 r=asprintf( &p_i->psz_name, "%s://%s:%d%s", url.psz_protocol,
374                           url.psz_host, url.i_port,
375                           url.psz_path ? url.psz_path : "" );
376             else
377                 r=asprintf( &p_i->psz_name, "%s://%s%s", url.psz_protocol,
378                           url.psz_host ? url.psz_host : "",
379                           url.psz_path ? url.psz_path : "" );
380         }
381         else
382         {
383             if( url.i_port > 0 )
384                 r=asprintf( &p_i->psz_name, "%s:%d%s", url.psz_host, url.i_port,
385                           url.psz_path ? url.psz_path : "" );
386             else
387                 r=asprintf( &p_i->psz_name, "%s%s", url.psz_host,
388                           url.psz_path ? url.psz_path : "" );
389         }
390         vlc_UrlClean( &url );
391         if( -1==r )
392             p_i->psz_name=NULL; /* recover from undefined value */
393     }
394
395     vlc_mutex_unlock( &p_i->lock );
396 }
397
398 mtime_t input_item_GetDuration( input_item_t *p_i )
399 {
400     vlc_mutex_lock( &p_i->lock );
401
402     mtime_t i_duration = p_i->i_duration;
403
404     vlc_mutex_unlock( &p_i->lock );
405     return i_duration;
406 }
407
408 void input_item_SetDuration( input_item_t *p_i, mtime_t i_duration )
409 {
410     bool b_send_event = false;
411
412     vlc_mutex_lock( &p_i->lock );
413     if( p_i->i_duration != i_duration )
414     {
415         p_i->i_duration = i_duration;
416         b_send_event = true;
417     }
418     vlc_mutex_unlock( &p_i->lock );
419
420     if( b_send_event )
421     {
422         vlc_event_t event;
423
424         event.type = vlc_InputItemDurationChanged;
425         event.u.input_item_duration_changed.new_duration = i_duration;
426         vlc_event_send( &p_i->event_manager, &event );
427     }
428 }
429
430
431 bool input_item_IsPreparsed( input_item_t *p_item )
432 {
433     vlc_mutex_lock( &p_item->lock );
434     bool b_preparsed = p_item->p_meta ? ( vlc_meta_GetStatus(p_item->p_meta) & ITEM_PREPARSED ) != 0 : false;
435     vlc_mutex_unlock( &p_item->lock );
436
437     return b_preparsed;
438 }
439
440 bool input_item_IsArtFetched( input_item_t *p_item )
441 {
442     vlc_mutex_lock( &p_item->lock );
443     bool b_fetched = p_item->p_meta ? ( vlc_meta_GetStatus(p_item->p_meta) & ITEM_ART_FETCHED ) != 0 : false;
444     vlc_mutex_unlock( &p_item->lock );
445
446     return b_fetched;
447 }
448
449 static void input_item_Destroy ( gc_object_t *p_gc )
450 {
451     input_item_t *p_item = vlc_priv( p_gc, input_item_t );
452
453     input_item_Clean( p_item );
454     free( p_item );
455 }
456
457 int input_item_AddOption( input_item_t *p_input, const char *psz_option,
458                           unsigned flags )
459 {
460     int err = VLC_SUCCESS;
461
462     if( psz_option == NULL )
463         return VLC_EGENERIC;
464
465     vlc_mutex_lock( &p_input->lock );
466     if (flags & VLC_INPUT_OPTION_UNIQUE)
467     {
468         for (int i = 0 ; i < p_input->i_options; i++)
469             if( !strcmp( p_input->ppsz_options[i], psz_option ) )
470                 goto out;
471     }
472
473     uint8_t *flagv = realloc (p_input->optflagv, p_input->optflagc + 1);
474     if (flagv == NULL)
475     {
476         err = VLC_ENOMEM;
477         goto out;
478     }
479     p_input->optflagv = flagv;
480     flagv[p_input->optflagc++] = flags;
481
482     INSERT_ELEM( p_input->ppsz_options, p_input->i_options,
483                  p_input->i_options, strdup( psz_option ) );
484 out:
485     vlc_mutex_unlock( &p_input->lock );
486     return err;
487 }
488
489 static info_category_t *InputItemFindCat( input_item_t *p_item,
490                                           int *pi_index, const char *psz_cat )
491 {
492     vlc_assert_locked( &p_item->lock );
493     for( int i = 0; i < p_item->i_categories && psz_cat; i++ )
494     {
495         info_category_t *p_cat = p_item->pp_categories[i];
496
497         if( !strcmp( p_cat->psz_name, psz_cat ) )
498         {
499             if( pi_index )
500                 *pi_index = i;
501             return p_cat;
502         }
503     }
504     return NULL;
505 }
506
507 /**
508  * Get a info item from a given category in a given input item.
509  *
510  * \param p_i The input item to get info from
511  * \param psz_cat String representing the category for the info
512  * \param psz_name String representing the name of the desired info
513  * \return A pointer to the string with the given info if found, or an
514  *         empty string otherwise. The caller should free the returned
515  *         pointer.
516  */
517 char *input_item_GetInfo( input_item_t *p_i,
518                           const char *psz_cat,
519                           const char *psz_name )
520 {
521     vlc_mutex_lock( &p_i->lock );
522
523     const info_category_t *p_cat = InputItemFindCat( p_i, NULL, psz_cat );
524     if( p_cat )
525     {
526         info_t *p_info = info_category_FindInfo( p_cat, NULL, psz_name );
527         if( p_info && p_info->psz_value )
528         {
529             char *psz_ret = strdup( p_info->psz_value );
530             vlc_mutex_unlock( &p_i->lock );
531             return psz_ret;
532         }
533     }
534     vlc_mutex_unlock( &p_i->lock );
535     return strdup( "" );
536 }
537
538 static int InputItemVaAddInfo( input_item_t *p_i,
539                                const char *psz_cat,
540                                const char *psz_name,
541                                const char *psz_format, va_list args )
542 {
543     vlc_assert_locked( &p_i->lock );
544
545     info_category_t *p_cat = InputItemFindCat( p_i, NULL, psz_cat );
546     if( !p_cat )
547     {
548         p_cat = info_category_New( psz_cat );
549         if( !p_cat )
550             return VLC_ENOMEM;
551         INSERT_ELEM( p_i->pp_categories, p_i->i_categories, p_i->i_categories,
552                      p_cat );
553     }
554     info_t *p_info = info_category_VaAddInfo( p_cat, psz_name, psz_format, args );
555     if( !p_info || !p_info->psz_value )
556         return VLC_EGENERIC;
557     return VLC_SUCCESS;
558 }
559
560 static int InputItemAddInfo( input_item_t *p_i,
561                              const char *psz_cat,
562                              const char *psz_name,
563                              const char *psz_format, ... )
564 {
565     va_list args;
566
567     va_start( args, psz_format );
568     const int i_ret = InputItemVaAddInfo( p_i, psz_cat, psz_name, psz_format, args );
569     va_end( args );
570
571     return i_ret;
572 }
573
574 int input_item_AddInfo( input_item_t *p_i,
575                         const char *psz_cat,
576                         const char *psz_name,
577                         const char *psz_format, ... )
578 {
579     va_list args;
580
581     vlc_mutex_lock( &p_i->lock );
582
583     va_start( args, psz_format );
584     const int i_ret = InputItemVaAddInfo( p_i, psz_cat, psz_name, psz_format, args );
585     va_end( args );
586
587     vlc_mutex_unlock( &p_i->lock );
588
589
590     if( !i_ret )
591     {
592         vlc_event_t event;
593
594         event.type = vlc_InputItemInfoChanged;
595         vlc_event_send( &p_i->event_manager, &event );
596     }
597     return i_ret;
598 }
599
600 int input_item_DelInfo( input_item_t *p_i,
601                         const char *psz_cat,
602                         const char *psz_name )
603 {
604     vlc_mutex_lock( &p_i->lock );
605     int i_cat;
606     info_category_t *p_cat = InputItemFindCat( p_i, &i_cat, psz_cat );
607     if( !p_cat )
608     {
609         vlc_mutex_unlock( &p_i->lock );
610         return VLC_EGENERIC;
611     }
612
613     if( psz_name )
614     {
615         /* Remove a specific info */
616         int i_ret = info_category_DeleteInfo( p_cat, psz_name );
617         if( i_ret )
618         {
619             vlc_mutex_unlock( &p_i->lock );
620             return VLC_EGENERIC;
621         }
622     }
623     else
624     {
625         /* Remove the complete categorie */
626         info_category_Delete( p_cat );
627         REMOVE_ELEM( p_i->pp_categories, p_i->i_categories, i_cat );
628     }
629     vlc_mutex_unlock( &p_i->lock );
630
631
632     vlc_event_t event;
633     event.type = vlc_InputItemInfoChanged;
634     vlc_event_send( &p_i->event_manager, &event );
635
636     return VLC_SUCCESS;
637 }
638 void input_item_ReplaceInfos( input_item_t *p_item, info_category_t *p_cat )
639 {
640     vlc_mutex_lock( &p_item->lock );
641     int i_cat;
642     info_category_t *p_old = InputItemFindCat( p_item, &i_cat, p_cat->psz_name );
643     if( p_old )
644     {
645         info_category_Delete( p_old );
646         p_item->pp_categories[i_cat] = p_cat;
647     }
648     else
649     {
650         INSERT_ELEM( p_item->pp_categories, p_item->i_categories, p_item->i_categories,
651                      p_cat );
652     }
653     vlc_mutex_unlock( &p_item->lock );
654
655
656     vlc_event_t event;
657     event.type = vlc_InputItemInfoChanged;
658     vlc_event_send( &p_item->event_manager, &event );
659 }
660 void input_item_MergeInfos( input_item_t *p_item, info_category_t *p_cat )
661 {
662     vlc_mutex_lock( &p_item->lock );
663     info_category_t *p_old = InputItemFindCat( p_item, NULL, p_cat->psz_name );
664     if( p_old )
665     {
666         for( int i = 0; i < p_cat->i_infos; i++ )
667             info_category_ReplaceInfo( p_old, p_cat->pp_infos[i] );
668         TAB_CLEAN( p_cat->i_infos, p_cat->pp_infos );
669         info_category_Delete( p_cat );
670     }
671     else
672     {
673         INSERT_ELEM( p_item->pp_categories, p_item->i_categories, p_item->i_categories,
674                      p_cat );
675     }
676     vlc_mutex_unlock( &p_item->lock );
677
678
679     vlc_event_t event;
680     event.type = vlc_InputItemInfoChanged;
681     vlc_event_send( &p_item->event_manager, &event );
682 }
683
684 #define EPG_DEBUG
685 void input_item_SetEpg( input_item_t *p_item, const vlc_epg_t *p_update )
686 {
687     vlc_mutex_lock( &p_item->lock );
688
689     /* */
690     vlc_epg_t *p_epg = NULL;
691     for( int i = 0; i < p_item->i_epg; i++ )
692     {
693         vlc_epg_t *p_tmp = p_item->pp_epg[i];
694
695         if( (p_tmp->psz_name == NULL) != (p_update->psz_name == NULL) )
696             continue;
697         if( p_tmp->psz_name && p_update->psz_name && strcmp(p_tmp->psz_name, p_update->psz_name) )
698             continue;
699
700         p_epg = p_tmp;
701         break;
702     }
703
704     /* */
705     if( !p_epg )
706     {
707         p_epg = vlc_epg_New( p_update->psz_name );
708         if( p_epg )
709             TAB_APPEND( p_item->i_epg, p_item->pp_epg, p_epg );
710     }
711     if( p_epg )
712         vlc_epg_Merge( p_epg, p_update );
713
714     vlc_mutex_unlock( &p_item->lock );
715
716     if( !p_epg )
717         return;
718
719 #ifdef EPG_DEBUG
720     char *psz_epg;
721     if( asprintf( &psz_epg, "EPG %s", p_epg->psz_name ? p_epg->psz_name : "unknown" ) < 0 )
722         goto signal;
723
724     input_item_DelInfo( p_item, psz_epg, NULL );
725
726     vlc_mutex_lock( &p_item->lock );
727     for( int i = 0; i < p_epg->i_event; i++ )
728     {
729         const vlc_epg_event_t *p_evt = p_epg->pp_event[i];
730         time_t t_start = (time_t)p_evt->i_start;
731         struct tm tm_start;
732         char psz_start[128];
733
734         localtime_r( &t_start, &tm_start );
735
736         snprintf( psz_start, sizeof(psz_start), "%4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d",
737                   1900 + tm_start.tm_year, 1 + tm_start.tm_mon, tm_start.tm_mday,
738                   tm_start.tm_hour, tm_start.tm_min, tm_start.tm_sec );
739         if( p_evt->psz_short_description || p_evt->psz_description )
740             InputItemAddInfo( p_item, psz_epg, psz_start, "%s (%2.2d:%2.2d) - %s %s",
741                               p_evt->psz_name,
742                               p_evt->i_duration/60/60, (p_evt->i_duration/60)%60,
743                               p_evt->psz_short_description ? p_evt->psz_short_description : "" ,
744                               p_evt->psz_description ? p_evt->psz_description : "" );
745         else
746             InputItemAddInfo( p_item, psz_epg, psz_start, "%s (%2.2d:%2.2d)",
747                               p_evt->psz_name,
748                               p_evt->i_duration/60/60, (p_evt->i_duration/60)%60 );
749     }
750     vlc_mutex_unlock( &p_item->lock );
751     free( psz_epg );
752 signal:
753 #endif
754
755     if( p_epg->i_event > 0 )
756     {
757         vlc_event_t event = { .type = vlc_InputItemInfoChanged, };
758         vlc_event_send( &p_item->event_manager, &event );
759     }
760 }
761
762 void input_item_SetEpgOffline( input_item_t *p_item )
763 {
764     vlc_mutex_lock( &p_item->lock );
765     for( int i = 0; i < p_item->i_epg; i++ )
766         vlc_epg_SetCurrent( p_item->pp_epg[i], -1 );
767     vlc_mutex_unlock( &p_item->lock );
768
769 #ifdef EPG_DEBUG
770     vlc_mutex_lock( &p_item->lock );
771     const int i_epg_info = p_item->i_epg;
772     char *ppsz_epg_info[i_epg_info];
773     for( int i = 0; i < p_item->i_epg; i++ )
774     {
775         const vlc_epg_t *p_epg = p_item->pp_epg[i];
776         if( asprintf( &ppsz_epg_info[i], "EPG %s", p_epg->psz_name ? p_epg->psz_name : "unknown" ) < 0 )
777             ppsz_epg_info[i] = NULL;
778     }
779     vlc_mutex_unlock( &p_item->lock );
780
781     for( int i = 0; i < i_epg_info; i++ )
782     {
783         if( !ppsz_epg_info[i] )
784             continue;
785         input_item_DelInfo( p_item, ppsz_epg_info[i], NULL );
786         free( ppsz_epg_info[i] );
787     }
788 #endif
789
790     vlc_event_t event = { .type = vlc_InputItemInfoChanged, };
791     vlc_event_send( &p_item->event_manager, &event );
792 }
793
794 input_item_t *input_item_NewExt( const char *psz_uri,
795                                  const char *psz_name,
796                                  int i_options,
797                                  const char *const *ppsz_options,
798                                  unsigned i_option_flags,
799                                  mtime_t i_duration )
800 {
801     return input_item_NewWithType( psz_uri, psz_name,
802                                   i_options, ppsz_options, i_option_flags,
803                                   i_duration, ITEM_TYPE_UNKNOWN );
804 }
805
806
807 input_item_t *
808 input_item_NewWithType( const char *psz_uri, const char *psz_name,
809                         int i_options, const char *const *ppsz_options,
810                         unsigned flags, mtime_t duration, int type )
811 {
812     static vlc_atomic_t last_input_id = VLC_ATOMIC_INIT(0);
813
814     input_item_t* p_input = calloc( 1, sizeof( *p_input ) );
815     if( !p_input )
816         return NULL;
817     vlc_event_manager_t * p_em = &p_input->event_manager;
818
819     p_input->i_id = vlc_atomic_inc(&last_input_id);
820     vlc_gc_init( p_input, input_item_Destroy );
821     vlc_mutex_init( &p_input->lock );
822
823     p_input->psz_name = NULL;
824     if( psz_name )
825         input_item_SetName( p_input, psz_name );
826
827     p_input->psz_uri = NULL;
828     if( psz_uri )
829         input_item_SetURI( p_input, psz_uri );
830     else
831         p_input->i_type = ITEM_TYPE_UNKNOWN;
832
833     TAB_INIT( p_input->i_options, p_input->ppsz_options );
834     p_input->optflagc = 0;
835     p_input->optflagv = NULL;
836     for( int i = 0; i < i_options; i++ )
837         input_item_AddOption( p_input, ppsz_options[i], flags );
838
839     p_input->i_duration = duration;
840     TAB_INIT( p_input->i_categories, p_input->pp_categories );
841     TAB_INIT( p_input->i_es, p_input->es );
842     p_input->p_stats = NULL;
843     p_input->i_nb_played = 0;
844     p_input->p_meta = NULL;
845     TAB_INIT( p_input->i_epg, p_input->pp_epg );
846
847     vlc_event_manager_init( p_em, p_input );
848     vlc_event_manager_register_event_type( p_em, vlc_InputItemMetaChanged );
849     vlc_event_manager_register_event_type( p_em, vlc_InputItemSubItemAdded );
850     vlc_event_manager_register_event_type( p_em, vlc_InputItemSubItemTreeAdded );
851     vlc_event_manager_register_event_type( p_em, vlc_InputItemDurationChanged );
852     vlc_event_manager_register_event_type( p_em, vlc_InputItemPreparsedChanged );
853     vlc_event_manager_register_event_type( p_em, vlc_InputItemNameChanged );
854     vlc_event_manager_register_event_type( p_em, vlc_InputItemInfoChanged );
855     vlc_event_manager_register_event_type( p_em, vlc_InputItemErrorWhenReadingChanged );
856
857     if( type != ITEM_TYPE_UNKNOWN )
858         p_input->i_type = type;
859     p_input->b_fixed_name = false;
860     p_input->b_error_when_reading = false;
861     return p_input;
862 }
863
864 input_item_t *input_item_Copy( input_item_t *p_input )
865 {
866     vlc_mutex_lock( &p_input->lock );
867
868     input_item_t *p_new_input =
869         input_item_NewWithType( p_input->psz_uri, p_input->psz_name,
870                                 0, NULL, 0, p_input->i_duration,
871                                 p_input->i_type );
872
873     if( p_new_input )
874     {
875         for( int i = 0 ; i< p_input->i_options; i++ )
876         {
877             input_item_AddOption( p_new_input,
878                                   p_input->ppsz_options[i],
879                                   p_input->optflagv[i] );
880         }
881
882         if( p_input->p_meta )
883         {
884             p_new_input->p_meta = vlc_meta_New();
885             vlc_meta_Merge( p_new_input->p_meta, p_input->p_meta );
886         }
887     }
888
889     vlc_mutex_unlock( &p_input->lock );
890
891     return p_new_input;
892 }
893
894 struct item_type_entry
895 {
896     const char psz_scheme[7];
897     uint8_t    i_type;
898 };
899
900 static int typecmp( const void *key, const void *entry )
901 {
902     const struct item_type_entry *type = entry;
903     const char *uri = key, *scheme = type->psz_scheme;
904
905     return strncmp( uri, scheme, strlen( scheme ) );
906 }
907
908 /* Guess the type of the item using the beginning of the mrl */
909 static int GuessType( const input_item_t *p_item )
910 {
911     static const struct item_type_entry tab[] =
912     {   /* /!\ Alphabetical order /!\ */
913         /* Short match work, not just exact match */
914         { "alsa",   ITEM_TYPE_CARD },
915         { "atsc",   ITEM_TYPE_CARD },
916         { "bd",     ITEM_TYPE_DISC },
917         { "cable",  ITEM_TYPE_CARD },
918         { "cdda",   ITEM_TYPE_CDDA },
919         { "cqam",   ITEM_TYPE_CARD },
920         { "dc1394", ITEM_TYPE_CARD },
921         { "dccp",   ITEM_TYPE_NET },
922         { "deckli", ITEM_TYPE_CARD }, /* decklink */
923         { "dir",    ITEM_TYPE_DIRECTORY },
924         { "dshow",  ITEM_TYPE_CARD },
925         { "dv",     ITEM_TYPE_CARD },
926         { "dvb",    ITEM_TYPE_CARD },
927         { "dvd",    ITEM_TYPE_DISC },
928         { "dtv",    ITEM_TYPE_CARD },
929         { "eyetv",  ITEM_TYPE_CARD },
930         { "fd",     ITEM_TYPE_UNKNOWN },
931         { "ftp",    ITEM_TYPE_NET },
932         { "http",   ITEM_TYPE_NET },
933         { "icyx",   ITEM_TYPE_NET },
934         { "imem",   ITEM_TYPE_UNKNOWN },
935         { "itpc",   ITEM_TYPE_NET },
936         { "jack",   ITEM_TYPE_CARD },
937         { "linsys", ITEM_TYPE_CARD },
938         { "live",   ITEM_TYPE_NET }, /* livedotcom */
939         { "mms",    ITEM_TYPE_NET },
940         { "mtp",    ITEM_TYPE_DISC },
941         { "ofdm",   ITEM_TYPE_CARD },
942         { "oss",    ITEM_TYPE_CARD },
943         { "pnm",    ITEM_TYPE_NET },
944         { "pvr",    ITEM_TYPE_CARD },
945         { "qam",    ITEM_TYPE_CARD },
946         { "qpsk",   ITEM_TYPE_CARD },
947         { "qtcapt", ITEM_TYPE_CARD }, /* qtcapture */
948         { "raw139", ITEM_TYPE_CARD }, /* raw1394 */
949         { "rt",     ITEM_TYPE_NET }, /* rtp, rtsp, rtmp */
950         { "satell", ITEM_TYPE_CARD }, /* sattelite */
951         { "screen", ITEM_TYPE_CARD },
952         { "sdp",    ITEM_TYPE_NET },
953         { "sftp",   ITEM_TYPE_NET },
954         { "shm",    ITEM_TYPE_CARD },
955         { "smb",    ITEM_TYPE_NET },
956         { "svcd",   ITEM_TYPE_DISC },
957         { "tcp",    ITEM_TYPE_NET },
958         { "terres", ITEM_TYPE_CARD }, /* terrestrial */
959         { "udp",    ITEM_TYPE_NET },  /* udplite too */
960         { "unsv",   ITEM_TYPE_NET },
961         { "usdigi", ITEM_TYPE_CARD }, /* usdigital */
962         { "v4l",    ITEM_TYPE_CARD },
963         { "vcd",    ITEM_TYPE_DISC },
964         { "window", ITEM_TYPE_CARD },
965     };
966     const struct item_type_entry *e;
967
968     if( !strstr( p_item->psz_uri, "://" ) )
969         return ITEM_TYPE_FILE;
970
971     e = bsearch( p_item->psz_uri, tab, sizeof( tab ) / sizeof( tab[0] ),
972                  sizeof( tab[0] ), typecmp );
973     return e ? e->i_type : ITEM_TYPE_FILE;
974 }
975
976 input_item_node_t *input_item_node_Create( input_item_t *p_input )
977 {
978     input_item_node_t* p_node = malloc( sizeof( input_item_node_t ) );
979     if( !p_node )
980         return NULL;
981
982     assert( p_input );
983
984     p_node->p_item = p_input;
985     vlc_gc_incref( p_input );
986
987     p_node->p_parent = NULL;
988     p_node->i_children = 0;
989     p_node->pp_children = NULL;
990
991     return p_node;
992 }
993
994 static void RecursiveNodeDelete( input_item_node_t *p_node )
995 {
996     for( int i = 0; i < p_node->i_children; i++ )
997         RecursiveNodeDelete( p_node->pp_children[i] );
998
999     vlc_gc_decref( p_node->p_item );
1000     free( p_node->pp_children );
1001     free( p_node );
1002 }
1003
1004 void input_item_node_Delete( input_item_node_t *p_node )
1005 {
1006     if( p_node->p_parent )
1007         for( int i = 0; i < p_node->p_parent->i_children; i++ )
1008             if( p_node->p_parent->pp_children[i] == p_node )
1009             {
1010                 REMOVE_ELEM( p_node->p_parent->pp_children,
1011                         p_node->p_parent->i_children,
1012                         i );
1013                 break;
1014             }
1015
1016     RecursiveNodeDelete( p_node );
1017 }
1018
1019 input_item_node_t *input_item_node_AppendItem( input_item_node_t *p_node, input_item_t *p_item )
1020 {
1021     input_item_node_t *p_new_child = input_item_node_Create( p_item );
1022     if( !p_new_child ) return NULL;
1023     input_item_node_AppendNode( p_node, p_new_child );
1024     return p_new_child;
1025 }
1026
1027 void input_item_node_AppendNode( input_item_node_t *p_parent, input_item_node_t *p_child )
1028 {
1029     assert( p_parent && p_child && p_child->p_parent == NULL );
1030     INSERT_ELEM( p_parent->pp_children,
1031                  p_parent->i_children,
1032                  p_parent->i_children,
1033                  p_child );
1034     p_child->p_parent = p_parent;
1035 }
1036
1037 void input_item_node_PostAndDelete( input_item_node_t *p_root )
1038 {
1039     post_subitems( p_root );
1040
1041     vlc_event_t event;
1042     event.type = vlc_InputItemSubItemTreeAdded;
1043     event.u.input_item_subitem_tree_added.p_root = p_root;
1044     vlc_event_send( &p_root->p_item->event_manager, &event );
1045
1046     input_item_node_Delete( p_root );
1047 }
1048
1049 /* Called by es_out when a new Elementary Stream is added or updated. */
1050 void input_item_UpdateTracksInfo(input_item_t *item, const es_format_t *fmt)
1051 {
1052     int i;
1053     es_format_t *fmt_copy = malloc(sizeof *fmt_copy);
1054     if (!fmt_copy)
1055         return;
1056
1057     es_format_Copy(fmt_copy, fmt);
1058     /* XXX: we could free p_extra to save memory, we will likely not need
1059      * the decoder specific data */
1060
1061     vlc_mutex_lock( &item->lock );
1062
1063     for( i = 0; i < item->i_es; i++ )
1064     {
1065         if (item->es[i]->i_id != fmt->i_id)
1066             continue;
1067
1068         /* We've found the right ES, replace it */
1069         es_format_Clean(item->es[i]);
1070         free(item->es[i]);
1071         item->es[i] = fmt_copy;
1072         vlc_mutex_unlock( &item->lock );
1073         return;
1074     }
1075
1076     /* ES not found, insert it */
1077     TAB_APPEND(item->i_es, item->es, fmt_copy);
1078     vlc_mutex_unlock( &item->lock );
1079 }