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