epg.c 8.67 KB
Newer Older
1 2 3
/*****************************************************************************
 * epg.c: Electronic Program Guide
 *****************************************************************************
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
4
 * Copyright (C) 2007 VLC authors and VideoLAN
5 6 7 8
 * $Id$
 *
 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
 *
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 27 28 29 30 31 32 33 34
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <vlc_common.h>
#include <vlc_epg.h>

François Cartegnie's avatar
François Cartegnie committed
35
static void vlc_epg_event_Clean(vlc_epg_event_t *p_event)
François Cartegnie's avatar
François Cartegnie committed
36
{
François Cartegnie's avatar
François Cartegnie committed
37 38 39
    free(p_event->psz_description);
    free(p_event->psz_short_description);
    free(p_event->psz_name);
François Cartegnie's avatar
François Cartegnie committed
40 41
}

François Cartegnie's avatar
François Cartegnie committed
42
void vlc_epg_event_Delete(vlc_epg_event_t *p_event)
43
{
François Cartegnie's avatar
François Cartegnie committed
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
    vlc_epg_event_Clean(p_event);
    free(p_event);
}

static void vlc_epg_event_Init(vlc_epg_event_t *p_event, uint16_t i_id,
                               int64_t i_start, uint32_t i_duration)
{
    memset(p_event, 0, sizeof(*p_event));
    p_event->i_start = i_start;
    p_event->i_id = i_id;
    p_event->i_duration = i_duration;
}

vlc_epg_event_t * vlc_epg_event_New(uint16_t i_id,
                                    int64_t i_start, uint32_t i_duration)
{
    vlc_epg_event_t *p_event = (vlc_epg_event_t *) malloc(sizeof(*p_event));
    if(p_event)
        vlc_epg_event_Init(p_event, i_id, i_start, i_duration);

    return p_event;
}

vlc_epg_event_t * vlc_epg_event_Duplicate( const vlc_epg_event_t *p_src )
{
    vlc_epg_event_t *p_evt = vlc_epg_event_New( p_src->i_id, p_src->i_start,
                                                p_src->i_duration );
71 72
    if( likely(p_evt) )
    {
François Cartegnie's avatar
François Cartegnie committed
73 74 75 76 77 78 79
        if( p_src->psz_description )
            p_evt->psz_description = strdup( p_src->psz_description );
        if( p_src->psz_name )
            p_evt->psz_name = strdup( p_src->psz_name );
        if( p_src->psz_short_description )
            p_evt->psz_short_description = strdup( p_src->psz_short_description );
        p_evt->i_rating = p_src->i_rating;
80 81 82 83
    }
    return p_evt;
}

François Cartegnie's avatar
François Cartegnie committed
84
static void vlc_epg_Init( vlc_epg_t *p_epg, uint32_t i_id, uint16_t i_source_id )
85
{
François Cartegnie's avatar
François Cartegnie committed
86 87 88
    p_epg->i_id = i_id;
    p_epg->i_source_id = i_source_id;
    p_epg->psz_name = NULL;
89 90 91 92
    p_epg->p_current = NULL;
    TAB_INIT( p_epg->i_event, p_epg->pp_event );
}

François Cartegnie's avatar
François Cartegnie committed
93
static void vlc_epg_Clean( vlc_epg_t *p_epg )
94
{
François Cartegnie's avatar
François Cartegnie committed
95
    size_t i;
96
    for( i = 0; i < p_epg->i_event; i++ )
François Cartegnie's avatar
François Cartegnie committed
97
        vlc_epg_event_Delete( p_epg->pp_event[i] );
98 99 100 101
    TAB_CLEAN( p_epg->i_event, p_epg->pp_event );
    free( p_epg->psz_name );
}

François Cartegnie's avatar
François Cartegnie committed
102
bool vlc_epg_AddEvent( vlc_epg_t *p_epg, vlc_epg_event_t *p_evt )
103
{
François Cartegnie's avatar
François Cartegnie committed
104
    ssize_t i_pos = -1;
105 106 107 108

    /* Insertions are supposed in sequential order first */
    if( p_epg->i_event )
    {
François Cartegnie's avatar
François Cartegnie committed
109
        if( p_epg->pp_event[0]->i_start > p_evt->i_start )
110 111 112
        {
            i_pos = 0;
        }
François Cartegnie's avatar
François Cartegnie committed
113
        else if ( p_epg->pp_event[p_epg->i_event - 1]->i_start >= p_evt->i_start )
114 115
        {
            /* Do bisect search lower start time entry */
François Cartegnie's avatar
François Cartegnie committed
116 117
            size_t i_lower = 0;
            size_t i_upper = p_epg->i_event - 1;
118 119 120

            while( i_lower < i_upper )
            {
François Cartegnie's avatar
François Cartegnie committed
121
                size_t i_split = ( i_lower + i_upper ) / 2;
122 123
                vlc_epg_event_t *p_cur = p_epg->pp_event[i_split];

François Cartegnie's avatar
François Cartegnie committed
124
                if( p_cur->i_start < p_evt->i_start )
125 126 127
                {
                    i_lower = i_split + 1;
                }
François Cartegnie's avatar
François Cartegnie committed
128
                else if ( p_cur->i_start >= p_evt->i_start )
129 130 131 132 133 134 135 136 137 138
                {
                    i_upper = i_split;
                }
            }
            i_pos = i_lower;
        }
    }

    if( i_pos != -1 )
    {
François Cartegnie's avatar
François Cartegnie committed
139 140
        /* There can be only one event at same time */
        if( p_epg->pp_event[i_pos]->i_start == p_evt->i_start )
141
        {
François Cartegnie's avatar
François Cartegnie committed
142
            vlc_epg_event_Delete( p_epg->pp_event[i_pos] );
143 144 145
            if( p_epg->p_current == p_epg->pp_event[i_pos] )
                p_epg->p_current = p_evt;
            p_epg->pp_event[i_pos] = p_evt;
François Cartegnie's avatar
François Cartegnie committed
146
            return true;
147 148 149 150 151 152 153
        }
        else
        {
            TAB_INSERT( p_epg->i_event, p_epg->pp_event, p_evt, i_pos );
        }
    }
    else
154
        TAB_APPEND( p_epg->i_event, p_epg->pp_event, p_evt );
François Cartegnie's avatar
François Cartegnie committed
155 156

    return true;
157 158
}

François Cartegnie's avatar
François Cartegnie committed
159
vlc_epg_t *vlc_epg_New( uint32_t i_id, uint16_t i_source_id )
160
{
Rémi Duraffort's avatar
Rémi Duraffort committed
161
    vlc_epg_t *p_epg = malloc( sizeof(*p_epg) );
162
    if( p_epg )
François Cartegnie's avatar
François Cartegnie committed
163
        vlc_epg_Init( p_epg, i_id, i_source_id );
164 165 166 167 168 169 170 171 172 173 174
    return p_epg;
}

void vlc_epg_Delete( vlc_epg_t *p_epg )
{
    vlc_epg_Clean( p_epg );
    free( p_epg );
}

void vlc_epg_SetCurrent( vlc_epg_t *p_epg, int64_t i_start )
{
François Cartegnie's avatar
François Cartegnie committed
175
    size_t i;
176 177 178 179 180 181 182 183 184 185 186 187 188 189
    p_epg->p_current = NULL;
    if( i_start < 0 )
        return;

    for( i = 0; i < p_epg->i_event; i++ )
    {
        if( p_epg->pp_event[i]->i_start == i_start )
        {
            p_epg->p_current = p_epg->pp_event[i];
            break;
        }
    }
}

190
static void vlc_epg_Prune( vlc_epg_t *p_dst )
191
{
192 193 194 195 196
    /* Keep only 1 old event  */
    if( p_dst->p_current )
    {
        while( p_dst->i_event > 1 && p_dst->pp_event[0] != p_dst->p_current && p_dst->pp_event[1] != p_dst->p_current )
        {
François Cartegnie's avatar
François Cartegnie committed
197
            vlc_epg_event_Delete( p_dst->pp_event[0] );
198 199 200 201 202 203 204 205 206
            TAB_ERASE( p_dst->i_event, p_dst->pp_event, 0 );
        }
    }
}

void vlc_epg_Merge( vlc_epg_t *p_dst_epg, const vlc_epg_t *p_src_epg )
{
    if( p_src_epg->i_event == 0 )
        return;
207

François Cartegnie's avatar
François Cartegnie committed
208 209
    size_t i_dst=0;
    size_t i_src=0;
210
    for( ; i_src < p_src_epg->i_event; i_src++ )
211
    {
212
        bool b_current = ( p_src_epg->pp_event[i_src] == p_src_epg->p_current );
213

François Cartegnie's avatar
François Cartegnie committed
214
        vlc_epg_event_t *p_src = vlc_epg_event_Duplicate( p_src_epg->pp_event[i_src] );
215 216 217 218 219
        if( unlikely(!p_src) )
            return;
        const int64_t i_src_end = p_src->i_start + p_src->i_duration;

        while( i_dst < p_dst_epg->i_event )
220
        {
221 222 223 224 225
            vlc_epg_event_t *p_dst = p_dst_epg->pp_event[i_dst];
            const int64_t i_dst_end = p_dst->i_start + p_dst->i_duration;

            /* appended is before current, no overlap */
            if( p_dst->i_start >= i_src_end )
226 227 228
            {
                break;
            }
229 230 231 232 233
            /* overlap case: appended would contain current's start (or are identical) */
            else if( ( p_dst->i_start >= p_src->i_start && p_dst->i_start < i_src_end ) ||
            /* overlap case: appended would contain current's end */
                    ( i_dst_end > p_src->i_start && i_dst_end <= i_src_end ) )
            {
François Cartegnie's avatar
François Cartegnie committed
234
                vlc_epg_event_Delete( p_dst );
235 236 237
                if( p_dst_epg->p_current == p_dst )
                {
                    b_current |= true;
238
                    p_dst_epg->p_current = NULL;
239
                }
240 241 242 243 244 245
                TAB_ERASE( p_dst_epg->i_event, p_dst_epg->pp_event, i_dst );
            }
            else
            {
                i_dst++;
            }
246
        }
247 248 249 250

        TAB_INSERT( p_dst_epg->i_event, p_dst_epg->pp_event, p_src, i_dst );
        if( b_current )
            p_dst_epg->p_current = p_src;
251 252
    }

253 254
    /* Remaining/trailing ones */
    for( ; i_src < p_src_epg->i_event; i_src++ )
255
    {
François Cartegnie's avatar
François Cartegnie committed
256
        vlc_epg_event_t *p_src = vlc_epg_event_Duplicate( p_src_epg->pp_event[i_src] );
257 258 259 260 261
        if( unlikely(!p_src) )
            return;
        TAB_APPEND( p_dst_epg->i_event, p_dst_epg->pp_event, p_src );
        if( p_src_epg->pp_event[i_src] == p_src_epg->p_current )
            p_dst_epg->p_current = p_src;
262 263
    }

264 265
    vlc_epg_Prune( p_dst_epg );
}
François Cartegnie's avatar
François Cartegnie committed
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285

vlc_epg_t * vlc_epg_Duplicate( const vlc_epg_t *p_src )
{
    vlc_epg_t *p_epg = vlc_epg_New( p_src->i_id, p_src->i_source_id );
    if( p_epg )
    {
        p_epg->psz_name = ( p_src->psz_name ) ? strdup( p_src->psz_name ) : NULL;
        for( size_t i=0; i<p_src->i_event; i++ )
        {
            vlc_epg_event_t *p_dup = vlc_epg_event_Duplicate( p_src->pp_event[i] );
            if( p_dup )
            {
                if( p_src->p_current == p_src->pp_event[i] )
                    p_epg->p_current = p_dup;
                TAB_APPEND( p_epg->i_event, p_epg->pp_event, p_dup );
            }
        }
    }
    return p_epg;
}