item.c 48.7 KB
Newer Older
1 2 3
/*****************************************************************************
 * item.c: input_item management
 *****************************************************************************
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
4
 * Copyright (C) 1998-2004 VLC authors and VideoLAN
5 6 7 8
 * $Id$
 *
 * Authors: Clément Stenac <zorglub@videolan.org>
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
9 10 11
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
12 13 14 15
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
16 17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
18
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
19 20 21
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 23
 *****************************************************************************/

24 25 26
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
27
#include <assert.h>
28
#include <time.h>
29
#include <limits.h>
30
#include <ctype.h>
31

32
#include <vlc_common.h>
33
#include <vlc_url.h>
34
#include <vlc_interface.h>
35
#include <vlc_charset.h>
36
#include <vlc_strings.h>
37

38
#include "item.h"
39
#include "info.h"
40
#include "input_internal.h"
Clément Stenac's avatar
Clément Stenac committed
41

42 43 44 45 46 47 48
struct input_item_opaque
{
    struct input_item_opaque *next;
    void *value;
    char name[1];
};

49
static int GuessType( const input_item_t *p_item, bool *p_net );
50

51 52 53 54 55 56 57 58 59 60
void input_item_SetErrorWhenReading( input_item_t *p_i, bool b_error )
{
    bool b_changed;

    vlc_mutex_lock( &p_i->lock );

    b_changed = p_i->b_error_when_reading != b_error;
    p_i->b_error_when_reading = b_error;

    vlc_mutex_unlock( &p_i->lock );
61

62 63
    if( b_changed )
    {
64 65 66
        vlc_event_send( &p_i->event_manager, &(vlc_event_t) {
            .type = vlc_InputItemErrorWhenReadingChanged,
            .u.input_item_error_when_reading_changed.new_value = b_error } );
67 68
    }
}
69
void input_item_SignalPreparseEnded( input_item_t *p_i, int status )
70
{
71 72 73
    vlc_event_send( &p_i->event_manager, &(vlc_event_t) {
        .type = vlc_InputItemPreparseEnded,
        .u.input_item_preparse_ended.new_status = status } );
74 75
}

76
void input_item_SetPreparsed( input_item_t *p_i, bool b_preparsed )
77
{
78
    bool b_send_event = false;
79

80
    vlc_mutex_lock( &p_i->lock );
81

82 83
    if( !p_i->p_meta )
        p_i->p_meta = vlc_meta_New();
84

85 86
    int status = vlc_meta_GetStatus(p_i->p_meta);
    int new_status;
87
    if( b_preparsed )
88
        new_status = status | ITEM_PREPARSED;
89
    else
90 91
        new_status = status & ~ITEM_PREPARSED;
    if( status != new_status )
92
    {
93
        vlc_meta_SetStatus(p_i->p_meta, new_status);
94 95 96 97 98 99 100
        b_send_event = true;
    }

    vlc_mutex_unlock( &p_i->lock );

    if( b_send_event )
    {
101 102 103
        vlc_event_send( &p_i->event_manager, &(vlc_event_t) {
            .type = vlc_InputItemPreparsedChanged,
            .u.input_item_preparsed_changed.new_status = new_status } );
104 105
    }
}
106

107 108 109 110 111 112 113
void input_item_SetArtNotFound( input_item_t *p_i, bool b_not_found )
{
    vlc_mutex_lock( &p_i->lock );

    if( !p_i->p_meta )
        p_i->p_meta = vlc_meta_New();

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 124
    vlc_mutex_unlock( &p_i->lock );
}
125

126 127 128 129 130 131 132
void input_item_SetArtFetched( input_item_t *p_i, bool b_art_fetched )
{
    vlc_mutex_lock( &p_i->lock );

    if( !p_i->p_meta )
        p_i->p_meta = vlc_meta_New();

133
    int status = vlc_meta_GetStatus(p_i->p_meta);
134

135
    if( b_art_fetched )
136
        status |= ITEM_ART_FETCHED;
137
    else
138 139 140
        status &= ~ITEM_ART_FETCHED;

    vlc_meta_SetStatus(p_i->p_meta, status);
141 142

    vlc_mutex_unlock( &p_i->lock );
143 144
}

145 146 147 148 149 150 151 152 153
void input_item_SetMeta( input_item_t *p_i, vlc_meta_type_t meta_type, const char *psz_val )
{
    vlc_mutex_lock( &p_i->lock );
    if( !p_i->p_meta )
        p_i->p_meta = vlc_meta_New();
    vlc_meta_Set( p_i->p_meta, meta_type, psz_val );
    vlc_mutex_unlock( &p_i->lock );

    /* Notify interested third parties */
154 155 156
    vlc_event_send( &p_i->event_manager, &(vlc_event_t) {
        .type = vlc_InputItemMetaChanged,
        .u.input_item_meta_changed.meta_type = meta_type } );
157 158
}

159 160
void input_item_CopyOptions( input_item_t *p_child,
                             input_item_t *p_parent )
161
{
162 163
    char **optv = NULL;
    uint8_t *flagv = NULL;
164 165 166
    int optc = 0;
    char **optv_realloc = NULL;
    uint8_t *flagv_realloc = NULL;
167

168 169
    vlc_mutex_lock( &p_parent->lock );

170
    if( p_parent->i_options > 0 )
171
    {
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
        optv = malloc( p_parent->i_options * sizeof (*optv) );
        if( likely(optv) )
            flagv = malloc( p_parent->i_options * sizeof (*flagv) );

        if( likely(flagv) )
        {
            for( int i = 0; i < p_parent->i_options; i++ )
            {
                char *psz_dup = strdup( p_parent->ppsz_options[i] );
                if( likely(psz_dup) )
                {
                    flagv[optc] = p_parent->optflagv[i];
                    optv[optc++] = psz_dup;
                }
            }
        }
188
    }
189

190
    vlc_mutex_unlock( &p_parent->lock );
191

192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
    if( likely(optv && flagv && optc ) )
    {
        vlc_mutex_lock( &p_child->lock );

        if( INT_MAX - p_child->i_options >= optc &&
            SIZE_MAX / sizeof (*flagv) >= (size_t) (p_child->i_options + optc) )
            flagv_realloc = realloc( p_child->optflagv,
                                    (p_child->i_options + optc) * sizeof (*flagv) );
        if( likely(flagv_realloc) )
        {
            p_child->optflagv = flagv_realloc;
            if( SIZE_MAX / sizeof (*optv) >= (size_t) (p_child->i_options + optc) )
                optv_realloc = realloc( p_child->ppsz_options,
                                       (p_child->i_options + optc) * sizeof (*optv) );
            if( likely(optv_realloc) )
            {
                p_child->ppsz_options = optv_realloc;
                memcpy( p_child->ppsz_options + p_child->i_options, optv,
                        optc * sizeof (*optv) );
                memcpy( p_child->optflagv + p_child->i_options, flagv,
                        optc * sizeof (*flagv) );
                p_child->i_options += optc;
                p_child->optflagc += optc;
            }
        }

        vlc_mutex_unlock( &p_child->lock );
    }
220

221 222 223 224 225 226
    if( unlikely(!flagv_realloc || !optv_realloc) )
    {
        /* Didn't copy pointers, so need to free the strdup() */
        for( int i=0; i<optc; i++ )
            free( optv[i] );
    }
227 228 229

    free( flagv );
    free( optv );
230 231
}

232
bool input_item_HasErrorWhenReading( input_item_t *p_item )
233
{
234 235 236 237 238 239 240
    vlc_mutex_lock( &p_item->lock );

    bool b_error = p_item->b_error_when_reading;

    vlc_mutex_unlock( &p_item->lock );

    return b_error;
241 242
}

243 244
bool input_item_MetaMatch( input_item_t *p_i,
                           vlc_meta_type_t meta_type, const char *psz )
245 246
{
    vlc_mutex_lock( &p_i->lock );
247

248 249 250 251 252
    if( !p_i->p_meta )
    {
        vlc_mutex_unlock( &p_i->lock );
        return false;
    }
253 254 255
    const char *psz_meta = vlc_meta_Get( p_i->p_meta, meta_type );
    bool b_ret = psz_meta && strcasestr( psz_meta, psz );

256 257
    vlc_mutex_unlock( &p_i->lock );

258
    return b_ret;
259 260
}

261
char *input_item_GetMeta( input_item_t *p_i, vlc_meta_type_t meta_type )
262 263 264 265 266 267 268 269 270
{
    vlc_mutex_lock( &p_i->lock );

    if( !p_i->p_meta )
    {
        vlc_mutex_unlock( &p_i->lock );
        return NULL;
    }

271
    char *psz = NULL;
272 273 274 275 276 277 278
    if( vlc_meta_Get( p_i->p_meta, meta_type ) )
        psz = strdup( vlc_meta_Get( p_i->p_meta, meta_type ) );

    vlc_mutex_unlock( &p_i->lock );
    return psz;
}

279 280 281 282 283 284 285 286
/* Get the title of a given item or fallback to the name if the title is empty */
char *input_item_GetTitleFbName( input_item_t *p_item )
{
    char *psz_ret;
    vlc_mutex_lock( &p_item->lock );

    if( !p_item->p_meta )
    {
287
        psz_ret = p_item->psz_name ? strdup( p_item->psz_name ) : NULL;
288
        vlc_mutex_unlock( &p_item->lock );
289
        return psz_ret;
290 291
    }

292 293 294
    const char *psz_title = vlc_meta_Get( p_item->p_meta, vlc_meta_Title );
    if( !EMPTY_STR( psz_title ) )
        psz_ret = strdup( psz_title );
295 296 297 298 299 300 301
    else
        psz_ret = p_item->psz_name ? strdup( p_item->psz_name ) : NULL;

    vlc_mutex_unlock( &p_item->lock );
    return psz_ret;
}

302
char *input_item_GetName( input_item_t *p_item )
303
{
304 305 306 307 308 309 310 311 312 313 314 315 316 317 318
    vlc_mutex_lock( &p_item->lock );

    char *psz_name = p_item->psz_name ? strdup( p_item->psz_name ) : NULL;

    vlc_mutex_unlock( &p_item->lock );
    return psz_name;
}
void input_item_SetName( input_item_t *p_item, const char *psz_name )
{
    vlc_mutex_lock( &p_item->lock );

    free( p_item->psz_name );
    p_item->psz_name = strdup( psz_name );

    vlc_mutex_unlock( &p_item->lock );
319 320
}

321
char *input_item_GetURI( input_item_t *p_i )
322 323
{
    vlc_mutex_lock( &p_i->lock );
324

325
    char *psz_s = p_i->psz_uri ? strdup( p_i->psz_uri ) : NULL;
326

327 328 329
    vlc_mutex_unlock( &p_i->lock );
    return psz_s;
}
330

331
void input_item_SetURI( input_item_t *p_i, const char *psz_uri )
332
{
333
    assert( psz_uri );
334
#ifndef NDEBUG
335 336 337 338
    if( !strstr( psz_uri, "://" )
     || strchr( psz_uri, ' ' ) || strchr( psz_uri, '"' ) )
        fprintf( stderr, "Warning: %s(\"%s\"): file path instead of URL.\n",
                 __func__, psz_uri );
339
#endif
340
    vlc_mutex_lock( &p_i->lock );
341 342
    free( p_i->psz_uri );
    p_i->psz_uri = strdup( psz_uri );
343

344
    p_i->i_type = GuessType( p_i, &p_i->b_net );
345

346 347 348 349
    if( p_i->psz_name )
        ;
    else
    if( p_i->i_type == ITEM_TYPE_FILE || p_i->i_type == ITEM_TYPE_DIRECTORY )
350
    {
351 352 353
        const char *psz_filename = strrchr( p_i->psz_uri, '/' );

        if( psz_filename && *psz_filename == '/' )
354 355 356
            psz_filename++;
        if( psz_filename && *psz_filename )
            p_i->psz_name = strdup( psz_filename );
357 358 359

        /* Make the name more readable */
        if( p_i->psz_name )
360
        {
361
            vlc_uri_decode( p_i->psz_name );
362 363
            EnsureUTF8( p_i->psz_name );
        }
364
    }
365 366
    else
    {   /* Strip login and password from title */
367
        int r;
368
        vlc_url_t url;
369

370
        vlc_UrlParse( &url, psz_uri );
371 372 373
        if( url.psz_protocol )
        {
            if( url.i_port > 0 )
374
                r=asprintf( &p_i->psz_name, "%s://%s:%d%s", url.psz_protocol,
375 376 377
                          url.psz_host, url.i_port,
                          url.psz_path ? url.psz_path : "" );
            else
378
                r=asprintf( &p_i->psz_name, "%s://%s%s", url.psz_protocol,
379 380 381 382 383 384
                          url.psz_host ? url.psz_host : "",
                          url.psz_path ? url.psz_path : "" );
        }
        else
        {
            if( url.i_port > 0 )
385
                r=asprintf( &p_i->psz_name, "%s:%d%s", url.psz_host, url.i_port,
386 387
                          url.psz_path ? url.psz_path : "" );
            else
388
                r=asprintf( &p_i->psz_name, "%s%s", url.psz_host,
389 390 391
                          url.psz_path ? url.psz_path : "" );
        }
        vlc_UrlClean( &url );
392 393
        if( -1==r )
            p_i->psz_name=NULL; /* recover from undefined value */
394
    }
395

396 397 398
    vlc_mutex_unlock( &p_i->lock );
}

399
mtime_t input_item_GetDuration( input_item_t *p_i )
400 401
{
    vlc_mutex_lock( &p_i->lock );
402

403
    mtime_t i_duration = p_i->i_duration;
404

405 406 407 408
    vlc_mutex_unlock( &p_i->lock );
    return i_duration;
}

409
void input_item_SetDuration( input_item_t *p_i, mtime_t i_duration )
410
{
411
    bool b_send_event = false;
412 413 414 415 416

    vlc_mutex_lock( &p_i->lock );
    if( p_i->i_duration != i_duration )
    {
        p_i->i_duration = i_duration;
417
        b_send_event = true;
418 419 420
    }
    vlc_mutex_unlock( &p_i->lock );

421
    if( b_send_event )
422
    {
423 424 425
        vlc_event_send( &p_i->event_manager, &(vlc_event_t) {
            .type = vlc_InputItemDurationChanged,
            .u.input_item_duration_changed.new_duration = i_duration } );
426 427 428
    }
}

429 430 431 432 433 434 435 436 437 438 439
char *input_item_GetNowPlayingFb( input_item_t *p_item )
{
    char *psz_meta = input_item_GetMeta( p_item, vlc_meta_NowPlaying );
    if( !psz_meta || strlen( psz_meta ) == 0 )
    {
        free( psz_meta );
        return input_item_GetMeta( p_item, vlc_meta_ESNowPlaying );
    }

    return psz_meta;
}
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 460 461 462 463 464 465 466 467 468 469
bool input_item_ShouldPreparseSubItems( input_item_t *p_item )
{
    bool b_ret;

    vlc_mutex_lock( &p_item->lock );
    b_ret = p_item->i_preparse_depth == -1 ? true : p_item->i_preparse_depth > 0;
    vlc_mutex_unlock( &p_item->lock );

    return b_ret;
}

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 475 476 477 478 479 480 481 482 483 484
    atomic_fetch_add( &owner->refs, 1 );
    return p_item;
}

void input_item_Release( input_item_t *p_item )
{
    input_item_owner_t *owner = item_owner(p_item);

    if( atomic_fetch_sub(&owner->refs, 1) != 1 )
        return;

485 486 487 488 489 490 491 492 493 494 495 496 497
    vlc_event_manager_fini( &p_item->event_manager );

    free( p_item->psz_name );
    free( p_item->psz_uri );
    if( p_item->p_stats != NULL )
    {
        vlc_mutex_destroy( &p_item->p_stats->lock );
        free( p_item->p_stats );
    }

    if( p_item->p_meta != NULL )
        vlc_meta_Delete( p_item->p_meta );

498 499 500 501 502 503
    for( input_item_opaque_t *o = p_item->opaques, *next; o != NULL; o = next )
    {
        next = o->next;
        free( o );
    }

504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523
    for( int i = 0; i < p_item->i_options; i++ )
        free( p_item->ppsz_options[i] );
    TAB_CLEAN( p_item->i_options, p_item->ppsz_options );
    free( p_item->optflagv );

    for( int i = 0; i < p_item->i_es; i++ )
    {
        es_format_Clean( p_item->es[i] );
        free( p_item->es[i] );
    }
    TAB_CLEAN( p_item->i_es, p_item->es );

    for( int i = 0; i < p_item->i_epg; i++ )
        vlc_epg_Delete( p_item->pp_epg[i] );
    TAB_CLEAN( p_item->i_epg, p_item->pp_epg );

    for( int i = 0; i < p_item->i_categories; i++ )
        info_category_Delete( p_item->pp_categories[i] );
    TAB_CLEAN( p_item->i_categories, p_item->pp_categories );

524 525 526 527
    for( int i = 0; i < p_item->i_slaves; i++ )
        input_item_slave_Delete( p_item->pp_slaves[i] );
    TAB_CLEAN( p_item->i_slaves, p_item->pp_slaves );

528 529
    vlc_mutex_destroy( &p_item->lock );
    free( owner );
530 531
}

532 533
int input_item_AddOption( input_item_t *p_input, const char *psz_option,
                          unsigned flags )
534
{
535 536
    int err = VLC_SUCCESS;

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 544 545
        for (int i = 0 ; i < p_input->i_options; i++)
            if( !strcmp( p_input->ppsz_options[i], psz_option ) )
                goto out;
546 547
    }

548 549 550 551 552 553
    uint8_t *flagv = realloc (p_input->optflagv, p_input->optflagc + 1);
    if (flagv == NULL)
    {
        err = VLC_ENOMEM;
        goto out;
    }
554

555
    p_input->optflagv = flagv;
556 557 558 559 560 561 562

    char* psz_option_dup = strdup( psz_option );
    if( unlikely( !psz_option_dup ) )
    {
        err = VLC_ENOMEM;
        goto out;
    }
563

564
    TAB_APPEND(p_input->i_options, p_input->ppsz_options, psz_option_dup);
565 566 567

    flagv[p_input->optflagc++] = flags;

568 569
out:
    vlc_mutex_unlock( &p_input->lock );
570
    return err;
571
}
572

573 574 575 576 577 578 579 580 581 582
int input_item_AddOptions( input_item_t *p_item, int i_options,
                           const char *const *ppsz_options,
                           unsigned i_flags )
{
    int i_ret = VLC_SUCCESS;
    for( int i = 0; i < i_options && i_ret == VLC_SUCCESS; i++ )
        i_ret = input_item_AddOption( p_item, ppsz_options[i], i_flags );
    return i_ret;
}

583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601
int input_item_AddOpaque(input_item_t *item, const char *name, void *value)
{
    assert(name != NULL);

    size_t namelen = strlen(name);
    input_item_opaque_t *entry = malloc(sizeof (*entry) + namelen);
    if (unlikely(entry == NULL))
        return VLC_ENOMEM;

    memcpy(entry->name, name, namelen + 1);
    entry->value = value;

    vlc_mutex_lock(&item->lock);
    entry->next = item->opaques;
    item->opaques = entry;
    vlc_mutex_unlock(&item->lock);
    return VLC_SUCCESS;
}

602 603 604 605 606 607 608 609
void input_item_ApplyOptions(vlc_object_t *obj, input_item_t *item)
{
    vlc_mutex_lock(&item->lock);
    assert(item->optflagc == (unsigned)item->i_options);

    for (unsigned i = 0; i < (unsigned)item->i_options; i++)
        var_OptionParse(obj, item->ppsz_options[i],
                        !!(item->optflagv[i] & VLC_INPUT_OPTION_TRUSTED));
610 611 612 613 614 615 616

    for (const input_item_opaque_t *o = item->opaques; o != NULL; o = o->next)
    {
        var_Create(obj, o->name, VLC_VAR_ADDRESS);
        var_SetAddress(obj, o->name, o->value);
    }

617 618 619
    vlc_mutex_unlock(&item->lock);
}

620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664
bool input_item_slave_GetType(const char *psz_filename,
                              enum slave_type *p_slave_type)
{
    static const char *const ppsz_sub_exts[] = { SLAVE_SPU_EXTENSIONS, NULL };
    static const char *const ppsz_audio_exts[] = { SLAVE_AUDIO_EXTENSIONS, NULL };

    static struct {
        enum slave_type i_type;
        const char *const *ppsz_exts;
    } p_slave_list[] = {
        { SLAVE_TYPE_SPU, ppsz_sub_exts },
        { SLAVE_TYPE_AUDIO, ppsz_audio_exts },
    };

    const char *psz_ext = strrchr(psz_filename, '.');
    if (psz_ext == NULL || *(++psz_ext) == '\0')
        return false;

    for (unsigned int i = 0; i < sizeof(p_slave_list) / sizeof(*p_slave_list); ++i)
    {
        for (const char *const *ppsz_slave_ext = p_slave_list[i].ppsz_exts;
             *ppsz_slave_ext != NULL; ppsz_slave_ext++)
        {
            if (!strcasecmp(psz_ext, *ppsz_slave_ext))
            {
                *p_slave_type = p_slave_list[i].i_type;
                return true;
            }
        }
    }
    return false;
}

input_item_slave_t *input_item_slave_New(const char *psz_uri, enum slave_type i_type,
                                       enum slave_priority i_priority)
{
    if( !psz_uri )
        return NULL;

    input_item_slave_t *p_slave = malloc( sizeof( *p_slave ) + strlen( psz_uri ) + 1 );
    if( !p_slave )
        return NULL;

    p_slave->i_type = i_type;
    p_slave->i_priority = i_priority;
665
    p_slave->b_forced = false;
666 667 668 669 670 671 672
    strcpy( p_slave->psz_uri, psz_uri );

    return p_slave;
}

int input_item_AddSlave(input_item_t *p_item, input_item_slave_t *p_slave)
{
673 674
    if( p_item == NULL || p_slave == NULL
     || p_slave->i_priority < SLAVE_PRIORITY_MATCH_NONE )
675 676 677 678
        return VLC_EGENERIC;

    vlc_mutex_lock( &p_item->lock );

679
    TAB_APPEND(p_item->i_slaves, p_item->pp_slaves, p_slave);
680 681 682 683 684

    vlc_mutex_unlock( &p_item->lock );
    return VLC_SUCCESS;
}

685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702
static info_category_t *InputItemFindCat( input_item_t *p_item,
                                          int *pi_index, const char *psz_cat )
{
    vlc_assert_locked( &p_item->lock );
    for( int i = 0; i < p_item->i_categories && psz_cat; i++ )
    {
        info_category_t *p_cat = p_item->pp_categories[i];

        if( !strcmp( p_cat->psz_name, psz_cat ) )
        {
            if( pi_index )
                *pi_index = i;
            return p_cat;
        }
    }
    return NULL;
}

703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718
/**
 * Get a info item from a given category in a given input item.
 *
 * \param p_i The input item to get info from
 * \param psz_cat String representing the category for the info
 * \param psz_name String representing the name of the desired info
 * \return A pointer to the string with the given info if found, or an
 *         empty string otherwise. The caller should free the returned
 *         pointer.
 */
char *input_item_GetInfo( input_item_t *p_i,
                          const char *psz_cat,
                          const char *psz_name )
{
    vlc_mutex_lock( &p_i->lock );

719 720
    const info_category_t *p_cat = InputItemFindCat( p_i, NULL, psz_cat );
    if( p_cat )
721
    {
722 723
        info_t *p_info = info_category_FindInfo( p_cat, NULL, psz_name );
        if( p_info && p_info->psz_value )
724
        {
725 726 727
            char *psz_ret = strdup( p_info->psz_value );
            vlc_mutex_unlock( &p_i->lock );
            return psz_ret;
728 729 730 731 732 733 734 735 736 737
        }
    }
    vlc_mutex_unlock( &p_i->lock );
    return strdup( "" );
}

static int InputItemVaAddInfo( input_item_t *p_i,
                               const char *psz_cat,
                               const char *psz_name,
                               const char *psz_format, va_list args )
738
{
739
    vlc_assert_locked( &p_i->lock );
740

741
    info_category_t *p_cat = InputItemFindCat( p_i, NULL, psz_cat );
742 743
    if( !p_cat )
    {
744 745
        p_cat = info_category_New( psz_cat );
        if( !p_cat )
746
            return VLC_ENOMEM;
747
        TAB_APPEND(p_i->i_categories, p_i->pp_categories, p_cat);
748
    }
749 750 751 752
    info_t *p_info = info_category_VaAddInfo( p_cat, psz_name, psz_format, args );
    if( !p_info || !p_info->psz_value )
        return VLC_EGENERIC;
    return VLC_SUCCESS;
753 754 755 756 757 758 759 760 761 762 763 764 765
}

int input_item_AddInfo( input_item_t *p_i,
                        const char *psz_cat,
                        const char *psz_name,
                        const char *psz_format, ... )
{
    va_list args;

    vlc_mutex_lock( &p_i->lock );

    va_start( args, psz_format );
    const int i_ret = InputItemVaAddInfo( p_i, psz_cat, psz_name, psz_format, args );
766 767 768 769
    va_end( args );

    vlc_mutex_unlock( &p_i->lock );

770 771

    if( !i_ret )
772 773
        vlc_event_send( &p_i->event_manager, &(vlc_event_t) {
            .type = vlc_InputItemInfoChanged } );
774

775
    return i_ret;
776
}
777

778 779 780 781 782
int input_item_DelInfo( input_item_t *p_i,
                        const char *psz_cat,
                        const char *psz_name )
{
    vlc_mutex_lock( &p_i->lock );
783 784 785
    int i_cat;
    info_category_t *p_cat = InputItemFindCat( p_i, &i_cat, psz_cat );
    if( !p_cat )
786 787 788 789 790 791 792 793
    {
        vlc_mutex_unlock( &p_i->lock );
        return VLC_EGENERIC;
    }

    if( psz_name )
    {
        /* Remove a specific info */
794 795
        int i_ret = info_category_DeleteInfo( p_cat, psz_name );
        if( i_ret )
796 797 798 799 800 801 802 803
        {
            vlc_mutex_unlock( &p_i->lock );
            return VLC_EGENERIC;
        }
    }
    else
    {
        /* Remove the complete categorie */
804
        info_category_Delete( p_cat );
805
        TAB_ERASE(p_i->i_categories, p_i->pp_categories, i_cat);
806 807 808
    }
    vlc_mutex_unlock( &p_i->lock );

809 810
    vlc_event_send( &p_i->event_manager,
                    &(vlc_event_t) { .type = vlc_InputItemInfoChanged } );
811 812 813

    return VLC_SUCCESS;
}
814 815 816 817 818 819 820 821 822 823 824
void input_item_ReplaceInfos( input_item_t *p_item, info_category_t *p_cat )
{
    vlc_mutex_lock( &p_item->lock );
    int i_cat;
    info_category_t *p_old = InputItemFindCat( p_item, &i_cat, p_cat->psz_name );
    if( p_old )
    {
        info_category_Delete( p_old );
        p_item->pp_categories[i_cat] = p_cat;
    }
    else
825
        TAB_APPEND(p_item->i_categories, p_item->pp_categories, p_cat);
826 827
    vlc_mutex_unlock( &p_item->lock );

828 829
    vlc_event_send( &p_item->event_manager,
                    &(vlc_event_t) { .type = vlc_InputItemInfoChanged } );
830
}
831

832 833 834 835 836 837 838 839 840 841 842 843
void input_item_MergeInfos( input_item_t *p_item, info_category_t *p_cat )
{
    vlc_mutex_lock( &p_item->lock );
    info_category_t *p_old = InputItemFindCat( p_item, NULL, p_cat->psz_name );
    if( p_old )
    {
        for( int i = 0; i < p_cat->i_infos; i++ )
            info_category_ReplaceInfo( p_old, p_cat->pp_infos[i] );
        TAB_CLEAN( p_cat->i_infos, p_cat->pp_infos );
        info_category_Delete( p_cat );
    }
    else
844
        TAB_APPEND(p_item->i_categories, p_item->pp_categories, p_cat);
845 846
    vlc_mutex_unlock( &p_item->lock );

847 848
    vlc_event_send( &p_item->event_manager,
                    &(vlc_event_t) { .type = vlc_InputItemInfoChanged } );
849
}
850

851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880
void input_item_SetEpgEvent( input_item_t *p_item, const vlc_epg_event_t *p_epg_evt )
{
    bool b_changed = false;
    vlc_mutex_lock( &p_item->lock );

    for( int i = 0; i < p_item->i_epg; i++ )
    {
        vlc_epg_t *p_epg = p_item->pp_epg[i];
        for( size_t j = 0; j < p_epg->i_event; j++ )
        {
            /* Same event can exist in more than one table */
            if( p_epg->pp_event[j]->i_id == p_epg_evt->i_id )
            {
                vlc_epg_event_t *p_dup = vlc_epg_event_Duplicate( p_epg_evt );
                if( p_dup )
                {
                    if( p_epg->p_current == p_epg->pp_event[j] )
                        p_epg->p_current = p_dup;
                    vlc_epg_event_Delete( p_epg->pp_event[j] );
                    p_epg->pp_event[j] = p_dup;
                    b_changed = true;
                }
                break;
            }
        }
    }
    vlc_mutex_unlock( &p_item->lock );

    if ( b_changed )
    {
881 882
        vlc_event_send( &p_item->event_manager,
                        &(vlc_event_t) { .type = vlc_InputItemInfoChanged } );
883 884 885
    }
}

886
//#define EPG_DEBUG
887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902
#ifdef EPG_DEBUG
static int InputItemAddInfo( input_item_t *p_i,
                             const char *psz_cat,
                             const char *psz_name,
                             const char *psz_format, ... )
{
    va_list args;

    va_start( args, psz_format );
    const int i_ret = InputItemVaAddInfo( p_i, psz_cat, psz_name, psz_format, args );
    va_end( args );

    return i_ret;
}
#endif

903
void input_item_SetEpg( input_item_t *p_item, const vlc_epg_t *p_update, bool b_current_source )
904
{
905 906 907 908
    vlc_epg_t *p_epg = vlc_epg_Duplicate( p_update );
    if( !p_epg )
        return;

909 910 911
    vlc_mutex_lock( &p_item->lock );

    /* */
912
    vlc_epg_t **pp_epg = NULL;
913 914
    for( int i = 0; i < p_item->i_epg; i++ )
    {
915 916
        if( p_item->pp_epg[i]->i_source_id == p_update->i_source_id &&
            p_item->pp_epg[i]->i_id == p_update->i_id )
François Cartegnie's avatar
François Cartegnie committed
917
        {
918
            pp_epg = &p_item->pp_epg[i];
François Cartegnie's avatar
François Cartegnie committed
919 920
            break;
        }
921 922
    }

923 924
    /* replace with new version */
    if( pp_epg )
925
    {
926
        vlc_epg_Delete( *pp_epg );
927 928
        if( *pp_epg == p_item->p_epg_table ) /* current table can have changed */
            p_item->p_epg_table = NULL;
929
        *pp_epg = p_epg;
930
    }
François Cartegnie's avatar
François Cartegnie committed
931 932
    else
    {
933
        TAB_APPEND( p_item->i_epg, p_item->pp_epg, p_epg );
François Cartegnie's avatar
François Cartegnie committed
934
    }
935

936 937 938
    if( b_current_source && p_epg->b_present )
        p_item->p_epg_table = p_epg;

939 940 941 942 943 944 945
    vlc_mutex_unlock( &p_item->lock );

#ifdef EPG_DEBUG
    char *psz_epg;
    if( asprintf( &psz_epg, "EPG %s", p_epg->psz_name ? p_epg->psz_name : "unknown" ) < 0 )
        goto signal;

946 947
    input_item_DelInfo( p_item, psz_epg, NULL );

948
    vlc_mutex_lock( &p_item->lock );
François Cartegnie's avatar
François Cartegnie committed
949
    for( size_t i = 0; i < p_epg->i_event; i++ )
950 951 952 953 954 955 956 957 958 959 960 961
    {
        const vlc_epg_event_t *p_evt = p_epg->pp_event[i];
        time_t t_start = (time_t)p_evt->i_start;
        struct tm tm_start;
        char psz_start[128];

        localtime_r( &t_start, &tm_start );

        snprintf( psz_start, sizeof(psz_start), "%4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d",
                  1900 + tm_start.tm_year, 1 + tm_start.tm_mon, tm_start.tm_mday,
                  tm_start.tm_hour, tm_start.tm_min, tm_start.tm_sec );
        if( p_evt->psz_short_description || p_evt->psz_description )
962
            InputItemAddInfo( p_item, psz_epg, psz_start, "%s (%2.2d:%2.2d) - %s %s",
963 964
                              p_evt->psz_name,
                              p_evt->i_duration/60/60, (p_evt->i_duration/60)%60,
965 966
                              p_evt->psz_short_description ? p_evt->psz_short_description : "" ,
                              p_evt->psz_description ? p_evt->psz_description : "" );
967
        else
968 969 970 971 972
            InputItemAddInfo( p_item, psz_epg, psz_start, "%s (%2.2d:%2.2d)",
                              p_evt->psz_name,
                              p_evt->i_duration/60/60, (p_evt->i_duration/60)%60 );
    }
    vlc_mutex_unlock( &p_item->lock );
973 974 975
    free( psz_epg );
signal:
#endif
976 977
    vlc_event_send( &p_item->event_manager,
                    &(vlc_event_t){ .type = vlc_InputItemInfoChanged, } );
978 979
}

980
void input_item_ChangeEPGSource( input_item_t *p_item, int i_source_id )
Laurent Aimar's avatar
Laurent Aimar committed
981 982
{
    vlc_mutex_lock( &p_item->lock );
983 984 985 986 987 988 989 990 991 992 993 994 995 996
    p_item->p_epg_table = NULL;
    if( i_source_id > 0 )
    {
        /* Update pointer to current/next table in the full schedule */
        for( int i = 0; i < p_item->i_epg; i++ )
        {
            if( p_item->pp_epg[i]->i_source_id == i_source_id &&
                p_item->pp_epg[i]->b_present )
            {
                p_item->p_epg_table = p_item->pp_epg[i];
                break;
            }
        }
    }
Laurent Aimar's avatar
Laurent Aimar committed
997
    vlc_mutex_unlock( &p_item->lock );
998 999
}

1000 1001 1002 1003 1004 1005 1006
void input_item_SetEpgTime( input_item_t *p_item, int64_t i_time )
{
    vlc_mutex_lock( &p_item->lock );
    p_item->i_epg_time = i_time;
    vlc_mutex_unlock( &p_item->lock );
}

1007 1008 1009
void input_item_SetEpgOffline( input_item_t *p_item )
{
    input_item_ChangeEPGSource( p_item, -1 );
Laurent Aimar's avatar
Laurent Aimar committed
1010 1011 1012 1013

#ifdef EPG_DEBUG
    vlc_mutex_lock( &p_item->lock );
    const int i_epg_info = p_item->i_epg;
1014
    if( i_epg_info > 0 )
Laurent Aimar's avatar
Laurent Aimar committed
1015
    {
1016 1017 1018 1019 1020 1021 1022 1023
        char *ppsz_epg_info[i_epg_info];
        for( int i = 0; i < p_item->i_epg; i++ )
        {
            const vlc_epg_t *p_epg = p_item->pp_epg[i];
            if( asprintf( &ppsz_epg_info[i], "EPG %s", p_epg->psz_name ? p_epg->psz_name : "unknown" ) < 0 )
                ppsz_epg_info[i] = NULL;
        }
        vlc_mutex_unlock( &p_item->lock );
Laurent Aimar's avatar
Laurent Aimar committed
1024

1025 1026 1027 1028 1029 1030 1031
        for( int i = 0; i < i_epg_info; i++ )
        {
            if( !ppsz_epg_info[i] )
                continue;
            input_item_DelInfo( p_item, ppsz_epg_info[i], NULL );
            free( ppsz_epg_info[i] );
        }
Laurent Aimar's avatar
Laurent Aimar committed
1032
    }
1033 1034
    else
        vlc_mutex_unlock( &p_item->lock );
Laurent Aimar's avatar
Laurent Aimar committed
1035 1036
#endif

1037 1038
    vlc_event_send( &p_item->event_manager,
                    &(vlc_event_t) { .type = vlc_InputItemInfoChanged } );
Laurent Aimar's avatar
Laurent Aimar committed
1039 1040
}

1041
input_item_t *
1042 1043
input_item_NewExt( const char *psz_uri, const char *psz_name,
                   mtime_t duration,<