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