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