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