]> git.sesse.net Git - vlc/blob - src/input/item.c
aout: remove legacy HAVE_FPU checks
[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     uint8_t *flagv = realloc (p_input->optflagv, p_input->optflagc + 1);
477     if (flagv == NULL)
478     {
479         err = VLC_ENOMEM;
480         goto out;
481     }
482     p_input->optflagv = flagv;
483     flagv[p_input->optflagc++] = flags;
484
485     INSERT_ELEM( p_input->ppsz_options, p_input->i_options,
486                  p_input->i_options, strdup( psz_option ) );
487 out:
488     vlc_mutex_unlock( &p_input->lock );
489     return err;
490 }
491
492 static info_category_t *InputItemFindCat( input_item_t *p_item,
493                                           int *pi_index, const char *psz_cat )
494 {
495     vlc_assert_locked( &p_item->lock );
496     for( int i = 0; i < p_item->i_categories && psz_cat; i++ )
497     {
498         info_category_t *p_cat = p_item->pp_categories[i];
499
500         if( !strcmp( p_cat->psz_name, psz_cat ) )
501         {
502             if( pi_index )
503                 *pi_index = i;
504             return p_cat;
505         }
506     }
507     return NULL;
508 }
509
510 /**
511  * Get a info item from a given category in a given input item.
512  *
513  * \param p_i The input item to get info from
514  * \param psz_cat String representing the category for the info
515  * \param psz_name String representing the name of the desired info
516  * \return A pointer to the string with the given info if found, or an
517  *         empty string otherwise. The caller should free the returned
518  *         pointer.
519  */
520 char *input_item_GetInfo( input_item_t *p_i,
521                           const char *psz_cat,
522                           const char *psz_name )
523 {
524     vlc_mutex_lock( &p_i->lock );
525
526     const info_category_t *p_cat = InputItemFindCat( p_i, NULL, psz_cat );
527     if( p_cat )
528     {
529         info_t *p_info = info_category_FindInfo( p_cat, NULL, psz_name );
530         if( p_info && p_info->psz_value )
531         {
532             char *psz_ret = strdup( p_info->psz_value );
533             vlc_mutex_unlock( &p_i->lock );
534             return psz_ret;
535         }
536     }
537     vlc_mutex_unlock( &p_i->lock );
538     return strdup( "" );
539 }
540
541 static int InputItemVaAddInfo( input_item_t *p_i,
542                                const char *psz_cat,
543                                const char *psz_name,
544                                const char *psz_format, va_list args )
545 {
546     vlc_assert_locked( &p_i->lock );
547
548     info_category_t *p_cat = InputItemFindCat( p_i, NULL, psz_cat );
549     if( !p_cat )
550     {
551         p_cat = info_category_New( psz_cat );
552         if( !p_cat )
553             return VLC_ENOMEM;
554         INSERT_ELEM( p_i->pp_categories, p_i->i_categories, p_i->i_categories,
555                      p_cat );
556     }
557     info_t *p_info = info_category_VaAddInfo( p_cat, psz_name, psz_format, args );
558     if( !p_info || !p_info->psz_value )
559         return VLC_EGENERIC;
560     return VLC_SUCCESS;
561 }
562
563 static int InputItemAddInfo( input_item_t *p_i,
564                              const char *psz_cat,
565                              const char *psz_name,
566                              const char *psz_format, ... )
567 {
568     va_list args;
569
570     va_start( args, psz_format );
571     const int i_ret = InputItemVaAddInfo( p_i, psz_cat, psz_name, psz_format, args );
572     va_end( args );
573
574     return i_ret;
575 }
576
577 int input_item_AddInfo( input_item_t *p_i,
578                         const char *psz_cat,
579                         const char *psz_name,
580                         const char *psz_format, ... )
581 {
582     va_list args;
583
584     vlc_mutex_lock( &p_i->lock );
585
586     va_start( args, psz_format );
587     const int i_ret = InputItemVaAddInfo( p_i, psz_cat, psz_name, psz_format, args );
588     va_end( args );
589
590     vlc_mutex_unlock( &p_i->lock );
591
592
593     if( !i_ret )
594     {
595         vlc_event_t event;
596
597         event.type = vlc_InputItemInfoChanged;
598         vlc_event_send( &p_i->event_manager, &event );
599     }
600     return i_ret;
601 }
602
603 int input_item_DelInfo( input_item_t *p_i,
604                         const char *psz_cat,
605                         const char *psz_name )
606 {
607     vlc_mutex_lock( &p_i->lock );
608     int i_cat;
609     info_category_t *p_cat = InputItemFindCat( p_i, &i_cat, psz_cat );
610     if( !p_cat )
611     {
612         vlc_mutex_unlock( &p_i->lock );
613         return VLC_EGENERIC;
614     }
615
616     if( psz_name )
617     {
618         /* Remove a specific info */
619         int i_ret = info_category_DeleteInfo( p_cat, psz_name );
620         if( i_ret )
621         {
622             vlc_mutex_unlock( &p_i->lock );
623             return VLC_EGENERIC;
624         }
625     }
626     else
627     {
628         /* Remove the complete categorie */
629         info_category_Delete( p_cat );
630         REMOVE_ELEM( p_i->pp_categories, p_i->i_categories, i_cat );
631     }
632     vlc_mutex_unlock( &p_i->lock );
633
634
635     vlc_event_t event;
636     event.type = vlc_InputItemInfoChanged;
637     vlc_event_send( &p_i->event_manager, &event );
638
639     return VLC_SUCCESS;
640 }
641 void input_item_ReplaceInfos( input_item_t *p_item, info_category_t *p_cat )
642 {
643     vlc_mutex_lock( &p_item->lock );
644     int i_cat;
645     info_category_t *p_old = InputItemFindCat( p_item, &i_cat, p_cat->psz_name );
646     if( p_old )
647     {
648         info_category_Delete( p_old );
649         p_item->pp_categories[i_cat] = p_cat;
650     }
651     else
652     {
653         INSERT_ELEM( p_item->pp_categories, p_item->i_categories, p_item->i_categories,
654                      p_cat );
655     }
656     vlc_mutex_unlock( &p_item->lock );
657
658
659     vlc_event_t event;
660     event.type = vlc_InputItemInfoChanged;
661     vlc_event_send( &p_item->event_manager, &event );
662 }
663 void input_item_MergeInfos( input_item_t *p_item, info_category_t *p_cat )
664 {
665     vlc_mutex_lock( &p_item->lock );
666     info_category_t *p_old = InputItemFindCat( p_item, NULL, p_cat->psz_name );
667     if( p_old )
668     {
669         for( int i = 0; i < p_cat->i_infos; i++ )
670             info_category_ReplaceInfo( p_old, p_cat->pp_infos[i] );
671         TAB_CLEAN( p_cat->i_infos, p_cat->pp_infos );
672         info_category_Delete( p_cat );
673     }
674     else
675     {
676         INSERT_ELEM( p_item->pp_categories, p_item->i_categories, p_item->i_categories,
677                      p_cat );
678     }
679     vlc_mutex_unlock( &p_item->lock );
680
681
682     vlc_event_t event;
683     event.type = vlc_InputItemInfoChanged;
684     vlc_event_send( &p_item->event_manager, &event );
685 }
686
687 #define EPG_DEBUG
688 void input_item_SetEpg( input_item_t *p_item, const vlc_epg_t *p_update )
689 {
690     vlc_mutex_lock( &p_item->lock );
691
692     /* */
693     vlc_epg_t *p_epg = NULL;
694     for( int i = 0; i < p_item->i_epg; i++ )
695     {
696         vlc_epg_t *p_tmp = p_item->pp_epg[i];
697
698         if( (p_tmp->psz_name == NULL) != (p_update->psz_name == NULL) )
699             continue;
700         if( p_tmp->psz_name && p_update->psz_name && strcmp(p_tmp->psz_name, p_update->psz_name) )
701             continue;
702
703         p_epg = p_tmp;
704         break;
705     }
706
707     /* */
708     if( !p_epg )
709     {
710         p_epg = vlc_epg_New( p_update->psz_name );
711         if( p_epg )
712             TAB_APPEND( p_item->i_epg, p_item->pp_epg, p_epg );
713     }
714     if( p_epg )
715         vlc_epg_Merge( p_epg, p_update );
716
717     vlc_mutex_unlock( &p_item->lock );
718
719     if( !p_epg )
720         return;
721
722 #ifdef EPG_DEBUG
723     char *psz_epg;
724     if( asprintf( &psz_epg, "EPG %s", p_epg->psz_name ? p_epg->psz_name : "unknown" ) < 0 )
725         goto signal;
726
727     input_item_DelInfo( p_item, psz_epg, NULL );
728
729     vlc_mutex_lock( &p_item->lock );
730     for( int i = 0; i < p_epg->i_event; i++ )
731     {
732         const vlc_epg_event_t *p_evt = p_epg->pp_event[i];
733         time_t t_start = (time_t)p_evt->i_start;
734         struct tm tm_start;
735         char psz_start[128];
736
737         localtime_r( &t_start, &tm_start );
738
739         snprintf( psz_start, sizeof(psz_start), "%4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d",
740                   1900 + tm_start.tm_year, 1 + tm_start.tm_mon, tm_start.tm_mday,
741                   tm_start.tm_hour, tm_start.tm_min, tm_start.tm_sec );
742         if( p_evt->psz_short_description || p_evt->psz_description )
743             InputItemAddInfo( p_item, psz_epg, psz_start, "%s (%2.2d:%2.2d) - %s %s",
744                               p_evt->psz_name,
745                               p_evt->i_duration/60/60, (p_evt->i_duration/60)%60,
746                               p_evt->psz_short_description ? p_evt->psz_short_description : "" ,
747                               p_evt->psz_description ? p_evt->psz_description : "" );
748         else
749             InputItemAddInfo( p_item, psz_epg, psz_start, "%s (%2.2d:%2.2d)",
750                               p_evt->psz_name,
751                               p_evt->i_duration/60/60, (p_evt->i_duration/60)%60 );
752     }
753     vlc_mutex_unlock( &p_item->lock );
754     free( psz_epg );
755 signal:
756 #endif
757
758     if( p_epg->i_event > 0 )
759     {
760         vlc_event_t event = { .type = vlc_InputItemInfoChanged, };
761         vlc_event_send( &p_item->event_manager, &event );
762     }
763 }
764
765 void input_item_SetEpgOffline( input_item_t *p_item )
766 {
767     vlc_mutex_lock( &p_item->lock );
768     for( int i = 0; i < p_item->i_epg; i++ )
769         vlc_epg_SetCurrent( p_item->pp_epg[i], -1 );
770     vlc_mutex_unlock( &p_item->lock );
771
772 #ifdef EPG_DEBUG
773     vlc_mutex_lock( &p_item->lock );
774     const int i_epg_info = p_item->i_epg;
775     char *ppsz_epg_info[i_epg_info];
776     for( int i = 0; i < p_item->i_epg; i++ )
777     {
778         const vlc_epg_t *p_epg = p_item->pp_epg[i];
779         if( asprintf( &ppsz_epg_info[i], "EPG %s", p_epg->psz_name ? p_epg->psz_name : "unknown" ) < 0 )
780             ppsz_epg_info[i] = NULL;
781     }
782     vlc_mutex_unlock( &p_item->lock );
783
784     for( int i = 0; i < i_epg_info; i++ )
785     {
786         if( !ppsz_epg_info[i] )
787             continue;
788         input_item_DelInfo( p_item, ppsz_epg_info[i], NULL );
789         free( ppsz_epg_info[i] );
790     }
791 #endif
792
793     vlc_event_t event = { .type = vlc_InputItemInfoChanged, };
794     vlc_event_send( &p_item->event_manager, &event );
795 }
796
797 input_item_t *input_item_NewExt( const char *psz_uri,
798                                  const char *psz_name,
799                                  int i_options,
800                                  const char *const *ppsz_options,
801                                  unsigned i_option_flags,
802                                  mtime_t i_duration )
803 {
804     return input_item_NewWithType( psz_uri, psz_name,
805                                   i_options, ppsz_options, i_option_flags,
806                                   i_duration, ITEM_TYPE_UNKNOWN );
807 }
808
809
810 input_item_t *
811 input_item_NewWithType( const char *psz_uri, const char *psz_name,
812                         int i_options, const char *const *ppsz_options,
813                         unsigned flags, mtime_t duration, int type )
814 {
815     static atomic_uint last_input_id = ATOMIC_VAR_INIT(0);
816
817     input_item_owner_t *owner = calloc( 1, sizeof( *owner ) );
818     if( unlikely(owner == NULL) )
819         return NULL;
820
821     atomic_init( &owner->refs, 1 );
822
823     input_item_t *p_input = &owner->item;
824     vlc_event_manager_t * p_em = &p_input->event_manager;
825
826     p_input->i_id = atomic_fetch_add(&last_input_id, 1);
827     vlc_mutex_init( &p_input->lock );
828
829     p_input->psz_name = NULL;
830     if( psz_name )
831         input_item_SetName( p_input, psz_name );
832
833     p_input->psz_uri = NULL;
834     if( psz_uri )
835         input_item_SetURI( p_input, psz_uri );
836     else
837         p_input->i_type = ITEM_TYPE_UNKNOWN;
838
839     TAB_INIT( p_input->i_options, p_input->ppsz_options );
840     p_input->optflagc = 0;
841     p_input->optflagv = NULL;
842     for( int i = 0; i < i_options; i++ )
843         input_item_AddOption( p_input, ppsz_options[i], flags );
844
845     p_input->i_duration = duration;
846     TAB_INIT( p_input->i_categories, p_input->pp_categories );
847     TAB_INIT( p_input->i_es, p_input->es );
848     p_input->p_stats = NULL;
849     p_input->i_nb_played = 0;
850     p_input->p_meta = NULL;
851     TAB_INIT( p_input->i_epg, p_input->pp_epg );
852
853     vlc_event_manager_init( p_em, p_input );
854     vlc_event_manager_register_event_type( p_em, vlc_InputItemMetaChanged );
855     vlc_event_manager_register_event_type( p_em, vlc_InputItemSubItemAdded );
856     vlc_event_manager_register_event_type( p_em, vlc_InputItemSubItemTreeAdded );
857     vlc_event_manager_register_event_type( p_em, vlc_InputItemDurationChanged );
858     vlc_event_manager_register_event_type( p_em, vlc_InputItemPreparsedChanged );
859     vlc_event_manager_register_event_type( p_em, vlc_InputItemNameChanged );
860     vlc_event_manager_register_event_type( p_em, vlc_InputItemInfoChanged );
861     vlc_event_manager_register_event_type( p_em, vlc_InputItemErrorWhenReadingChanged );
862
863     if( type != ITEM_TYPE_UNKNOWN )
864         p_input->i_type = type;
865     p_input->b_fixed_name = false;
866     p_input->b_error_when_reading = false;
867     return p_input;
868 }
869
870 input_item_t *input_item_Copy( input_item_t *p_input )
871 {
872     vlc_mutex_lock( &p_input->lock );
873
874     input_item_t *p_new_input =
875         input_item_NewWithType( p_input->psz_uri, p_input->psz_name,
876                                 0, NULL, 0, p_input->i_duration,
877                                 p_input->i_type );
878
879     if( p_new_input )
880     {
881         for( int i = 0 ; i< p_input->i_options; i++ )
882         {
883             input_item_AddOption( p_new_input,
884                                   p_input->ppsz_options[i],
885                                   p_input->optflagv[i] );
886         }
887
888         if( p_input->p_meta )
889         {
890             p_new_input->p_meta = vlc_meta_New();
891             vlc_meta_Merge( p_new_input->p_meta, p_input->p_meta );
892         }
893     }
894
895     vlc_mutex_unlock( &p_input->lock );
896
897     return p_new_input;
898 }
899
900 struct item_type_entry
901 {
902     const char psz_scheme[7];
903     uint8_t    i_type;
904 };
905
906 static int typecmp( const void *key, const void *entry )
907 {
908     const struct item_type_entry *type = entry;
909     const char *uri = key, *scheme = type->psz_scheme;
910
911     return strncmp( uri, scheme, strlen( scheme ) );
912 }
913
914 /* Guess the type of the item using the beginning of the mrl */
915 static int GuessType( const input_item_t *p_item )
916 {
917     static const struct item_type_entry tab[] =
918     {   /* /!\ Alphabetical order /!\ */
919         /* Short match work, not just exact match */
920         { "alsa",   ITEM_TYPE_CARD },
921         { "atsc",   ITEM_TYPE_CARD },
922         { "bd",     ITEM_TYPE_DISC },
923         { "cable",  ITEM_TYPE_CARD },
924         { "cdda",   ITEM_TYPE_CDDA },
925         { "cqam",   ITEM_TYPE_CARD },
926         { "dc1394", ITEM_TYPE_CARD },
927         { "dccp",   ITEM_TYPE_NET },
928         { "deckli", ITEM_TYPE_CARD }, /* decklink */
929         { "dir",    ITEM_TYPE_DIRECTORY },
930         { "dshow",  ITEM_TYPE_CARD },
931         { "dv",     ITEM_TYPE_CARD },
932         { "dvb",    ITEM_TYPE_CARD },
933         { "dvd",    ITEM_TYPE_DISC },
934         { "dtv",    ITEM_TYPE_CARD },
935         { "eyetv",  ITEM_TYPE_CARD },
936         { "fd",     ITEM_TYPE_UNKNOWN },
937         { "ftp",    ITEM_TYPE_NET },
938         { "http",   ITEM_TYPE_NET },
939         { "icyx",   ITEM_TYPE_NET },
940         { "imem",   ITEM_TYPE_UNKNOWN },
941         { "itpc",   ITEM_TYPE_NET },
942         { "jack",   ITEM_TYPE_CARD },
943         { "linsys", ITEM_TYPE_CARD },
944         { "live",   ITEM_TYPE_NET }, /* livedotcom */
945         { "mms",    ITEM_TYPE_NET },
946         { "mtp",    ITEM_TYPE_DISC },
947         { "ofdm",   ITEM_TYPE_CARD },
948         { "oss",    ITEM_TYPE_CARD },
949         { "pnm",    ITEM_TYPE_NET },
950         { "qam",    ITEM_TYPE_CARD },
951         { "qpsk",   ITEM_TYPE_CARD },
952         { "qtcapt", ITEM_TYPE_CARD }, /* qtcapture */
953         { "raw139", ITEM_TYPE_CARD }, /* raw1394 */
954         { "rt",     ITEM_TYPE_NET }, /* rtp, rtsp, rtmp */
955         { "satell", ITEM_TYPE_CARD }, /* sattelite */
956         { "screen", ITEM_TYPE_CARD },
957         { "sdp",    ITEM_TYPE_NET },
958         { "sftp",   ITEM_TYPE_NET },
959         { "shm",    ITEM_TYPE_CARD },
960         { "smb",    ITEM_TYPE_NET },
961         { "svcd",   ITEM_TYPE_DISC },
962         { "tcp",    ITEM_TYPE_NET },
963         { "terres", ITEM_TYPE_CARD }, /* terrestrial */
964         { "udp",    ITEM_TYPE_NET },  /* udplite too */
965         { "unsv",   ITEM_TYPE_NET },
966         { "usdigi", ITEM_TYPE_CARD }, /* usdigital */
967         { "v4l",    ITEM_TYPE_CARD },
968         { "vcd",    ITEM_TYPE_DISC },
969         { "window", ITEM_TYPE_CARD },
970     };
971     const struct item_type_entry *e;
972
973     if( !strstr( p_item->psz_uri, "://" ) )
974         return ITEM_TYPE_FILE;
975
976     e = bsearch( p_item->psz_uri, tab, sizeof( tab ) / sizeof( tab[0] ),
977                  sizeof( tab[0] ), typecmp );
978     return e ? e->i_type : ITEM_TYPE_FILE;
979 }
980
981 input_item_node_t *input_item_node_Create( input_item_t *p_input )
982 {
983     input_item_node_t* p_node = malloc( sizeof( input_item_node_t ) );
984     if( !p_node )
985         return NULL;
986
987     assert( p_input );
988
989     p_node->p_item = p_input;
990     vlc_gc_incref( p_input );
991
992     p_node->p_parent = NULL;
993     p_node->i_children = 0;
994     p_node->pp_children = NULL;
995
996     return p_node;
997 }
998
999 static void RecursiveNodeDelete( input_item_node_t *p_node )
1000 {
1001     for( int i = 0; i < p_node->i_children; i++ )
1002         RecursiveNodeDelete( p_node->pp_children[i] );
1003
1004     vlc_gc_decref( p_node->p_item );
1005     free( p_node->pp_children );
1006     free( p_node );
1007 }
1008
1009 void input_item_node_Delete( input_item_node_t *p_node )
1010 {
1011     if( p_node->p_parent )
1012         for( int i = 0; i < p_node->p_parent->i_children; i++ )
1013             if( p_node->p_parent->pp_children[i] == p_node )
1014             {
1015                 REMOVE_ELEM( p_node->p_parent->pp_children,
1016                         p_node->p_parent->i_children,
1017                         i );
1018                 break;
1019             }
1020
1021     RecursiveNodeDelete( p_node );
1022 }
1023
1024 input_item_node_t *input_item_node_AppendItem( input_item_node_t *p_node, input_item_t *p_item )
1025 {
1026     input_item_node_t *p_new_child = input_item_node_Create( p_item );
1027     if( !p_new_child ) return NULL;
1028     input_item_node_AppendNode( p_node, p_new_child );
1029     return p_new_child;
1030 }
1031
1032 void input_item_node_AppendNode( input_item_node_t *p_parent, input_item_node_t *p_child )
1033 {
1034     assert( p_parent && p_child && p_child->p_parent == NULL );
1035     INSERT_ELEM( p_parent->pp_children,
1036                  p_parent->i_children,
1037                  p_parent->i_children,
1038                  p_child );
1039     p_child->p_parent = p_parent;
1040 }
1041
1042 void input_item_node_PostAndDelete( input_item_node_t *p_root )
1043 {
1044     post_subitems( p_root );
1045
1046     vlc_event_t event;
1047     event.type = vlc_InputItemSubItemTreeAdded;
1048     event.u.input_item_subitem_tree_added.p_root = p_root;
1049     vlc_event_send( &p_root->p_item->event_manager, &event );
1050
1051     input_item_node_Delete( p_root );
1052 }
1053
1054 /* Called by es_out when a new Elementary Stream is added or updated. */
1055 void input_item_UpdateTracksInfo(input_item_t *item, const es_format_t *fmt)
1056 {
1057     int i;
1058     es_format_t *fmt_copy = malloc(sizeof *fmt_copy);
1059     if (!fmt_copy)
1060         return;
1061
1062     es_format_Copy(fmt_copy, fmt);
1063     /* XXX: we could free p_extra to save memory, we will likely not need
1064      * the decoder specific data */
1065
1066     vlc_mutex_lock( &item->lock );
1067
1068     for( i = 0; i < item->i_es; i++ )
1069     {
1070         if (item->es[i]->i_id != fmt->i_id)
1071             continue;
1072
1073         /* We've found the right ES, replace it */
1074         es_format_Clean(item->es[i]);
1075         free(item->es[i]);
1076         item->es[i] = fmt_copy;
1077         vlc_mutex_unlock( &item->lock );
1078         return;
1079     }
1080
1081     /* ES not found, insert it */
1082     TAB_APPEND(item->i_es, item->es, fmt_copy);
1083     vlc_mutex_unlock( &item->lock );
1084 }