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