clock.c 15.2 KB
Newer Older
1
/*****************************************************************************
2
 * clock.c: Clock/System date convertions, stream management
3
 *****************************************************************************
Laurent Aimar's avatar
Laurent Aimar committed
4
 * Copyright (C) 1999-2008 the VideoLAN team
5
 * Copyright (C) 2008 Laurent Aimar
6
 * $Id$
7 8
 *
 * Authors: Christophe Massiot <massiot@via.ecp.fr>
Laurent Aimar's avatar
Laurent Aimar committed
9
 *          Laurent Aimar < fenrir _AT_ videolan _DOT_ org >
10 11 12 13 14
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
15
 *
16 17 18 19 20 21 22
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
Antoine Cellerier's avatar
Antoine Cellerier committed
23
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 25 26 27 28
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
29 30 31 32
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

33
#include <vlc_common.h>
34
#include <vlc_input.h>
35
#include "clock.h"
36
#include <assert.h>
37

38 39 40 41 42
/* TODO:
 * - clean up locking once clock code is stable
 *
 */

43
/*
44
 * DISCUSSION : SYNCHRONIZATION METHOD
45
 *
46 47 48 49 50 51 52
 * In some cases we can impose the pace of reading (when reading from a
 * file or a pipe), and for the synchronization we simply sleep() until
 * it is time to deliver the packet to the decoders. When reading from
 * the network, we must be read at the same pace as the server writes,
 * otherwise the kernel's buffer will trash packets. The risk is now to
 * overflow the input buffers in case the server goes too fast, that is
 * why we do these calculations :
53
 *
54 55 56 57
 * We compute a mean for the pcr because we want to eliminate the
 * network jitter and keep the low frequency variations. The mean is
 * in fact a low pass filter and the jitter is a high frequency signal
 * that is why it is eliminated by the filter/average.
58
 *
59 60 61 62 63 64 65
 * The low frequency variations enable us to synchronize the client clock
 * with the server clock because they represent the time variation between
 * the 2 clocks. Those variations (ie the filtered pcr) are used to compute
 * the presentation dates for the audio and video frames. With those dates
 * we can decode (or trash) the MPEG2 stream at "exactly" the same rate
 * as it is sent by the server and so we keep the synchronization between
 * the server and the client.
66
 *
67 68
 * It is a very important matter if you want to avoid underflow or overflow
 * in all the FIFOs, but it may be not enough.
69 70
 */

Laurent Aimar's avatar
Laurent Aimar committed
71
/* i_cr_average : Maximum number of samples used to compute the
72 73 74 75 76
 * dynamic average value.
 * We use the following formula :
 * new_average = (old_average * c_average + new_sample_value) / (c_average +1)
 */

77

78
/*****************************************************************************
79
 * Constants
80
 *****************************************************************************/
81 82

/* Maximum gap allowed between two CRs. */
83
#define CR_MAX_GAP (INT64_C(2000000)*100/9)
84

85 86
/* Latency introduced on DVDs with CR == 0 on chapter change - this is from
 * my dice --Meuuh */
Laurent Aimar's avatar
Laurent Aimar committed
87
#define CR_MEAN_PTS_GAP (300000)
88

89 90 91
/*****************************************************************************
 * Structures
 *****************************************************************************/
92 93 94 95 96 97 98 99 100 101 102 103

/**
 * This structure holds long term average
 */
typedef struct
{
    mtime_t i_value;
    int     i_residue;

    int     i_count;
    int     i_divider;
} average_t;
104 105
static void    AvgInit( average_t *, int i_divider );
static void    AvgClean( average_t * );
106

107 108 109
static void    AvgReset( average_t * );
static void    AvgUpdate( average_t *, mtime_t i_value );
static mtime_t AvgGet( average_t * );
110
static void    AvgRescale( average_t *, int i_divider );
111

Laurent Aimar's avatar
Laurent Aimar committed
112 113 114 115 116 117 118 119 120 121 122 123 124
/* */
typedef struct
{
    mtime_t i_stream;
    mtime_t i_system;
} clock_point_t;

static inline clock_point_t clock_point_Create( mtime_t i_stream, mtime_t i_system )
{
    clock_point_t p = { .i_stream = i_stream, .i_system = i_system };
    return p;
}

125
/* */
126 127
struct input_clock_t
{
128 129 130
    /* */
    vlc_mutex_t lock;

Laurent Aimar's avatar
Laurent Aimar committed
131
    /* Reference point */
Laurent Aimar's avatar
Laurent Aimar committed
132 133
    bool          b_has_reference;
    clock_point_t ref;
Laurent Aimar's avatar
Laurent Aimar committed
134 135 136

    /* Last point
     * It is used to detect unexpected stream discontinuities */
Laurent Aimar's avatar
Laurent Aimar committed
137
    clock_point_t last;
138

139
    /* Maximal timestamp returned by input_clock_GetTS (in system unit) */
140
    mtime_t i_ts_max;
141

Laurent Aimar's avatar
Laurent Aimar committed
142
    /* Clock drift */
143 144
    mtime_t i_next_drift_update;
    average_t drift;
145

Laurent Aimar's avatar
Laurent Aimar committed
146 147
    /* Current modifiers */
    int     i_rate;
148
    mtime_t i_pts_delay;
149 150
    bool    b_paused;
    mtime_t i_pause_date;
151 152
};

Laurent Aimar's avatar
Laurent Aimar committed
153
static mtime_t ClockStreamToSystem( input_clock_t *, mtime_t i_stream );
Laurent Aimar's avatar
Laurent Aimar committed
154
static mtime_t ClockSystemToStream( input_clock_t *, mtime_t i_system );
155

156
/*****************************************************************************
Laurent Aimar's avatar
Laurent Aimar committed
157
 * input_clock_New: create a new clock
158
 *****************************************************************************/
159
input_clock_t *input_clock_New( int i_rate )
160
{
161 162 163 164
    input_clock_t *cl = malloc( sizeof(*cl) );
    if( !cl )
        return NULL;

165
    vlc_mutex_init( &cl->lock );
166
    cl->b_has_reference = false;
Laurent Aimar's avatar
Laurent Aimar committed
167
    cl->ref = clock_point_Create( 0, 0 );
168

Laurent Aimar's avatar
Laurent Aimar committed
169
    cl->last = clock_point_Create( 0, 0 );
170 171

    cl->i_ts_max = 0;
172

173
    cl->i_next_drift_update = 0;
174
    AvgInit( &cl->drift, 10 );
175

Laurent Aimar's avatar
Laurent Aimar committed
176
    cl->i_rate = i_rate;
177
    cl->i_pts_delay = 0;
178 179
    cl->b_paused = false;
    cl->i_pause_date = 0;
Laurent Aimar's avatar
Laurent Aimar committed
180

181 182 183 184
    return cl;
}

/*****************************************************************************
Laurent Aimar's avatar
Laurent Aimar committed
185
 * input_clock_Delete: destroy a new clock
186
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
187
void input_clock_Delete( input_clock_t *cl )
188
{
189
    AvgClean( &cl->drift );
190
    vlc_mutex_destroy( &cl->lock );
191
    free( cl );
192 193 194
}

/*****************************************************************************
195
 * input_clock_Update: manages a clock reference
196 197 198
 *
 *  i_ck_stream: date in stream clock
 *  i_ck_system: date in system clock
199
 *****************************************************************************/
200
void input_clock_Update( input_clock_t *cl,
201
                         vlc_object_t *p_log, bool b_can_pace_control,
Laurent Aimar's avatar
Laurent Aimar committed
202
                         mtime_t i_ck_stream, mtime_t i_ck_system )
203
{
204
    bool b_reset_reference = false;
205

206
    vlc_mutex_lock( &cl->lock );
207

208
    if( ( !cl->b_has_reference ) ||
Laurent Aimar's avatar
Laurent Aimar committed
209
        ( i_ck_stream == 0 && cl->last.i_stream != 0 ) )
210
    {
211 212
        /* */
        b_reset_reference= true;
213
    }
Laurent Aimar's avatar
Laurent Aimar committed
214 215 216
    else if( cl->last.i_stream != 0 &&
             ( (cl->last.i_stream - i_ck_stream) > CR_MAX_GAP ||
               (cl->last.i_stream - i_ck_stream) < -CR_MAX_GAP ) )
217
    {
218 219 220
        /* Stream discontinuity, for which we haven't received a
         * warning from the stream control facilities (dd-edited
         * stream ?). */
221
        msg_Warn( p_log, "clock gap, unexpected stream discontinuity" );
222
        cl->i_ts_max = 0;
223 224

        /* */
225
        msg_Warn( p_log, "feeding synchro with a new reference point trying to recover from clock gap" );
226 227 228 229
        b_reset_reference= true;
    }
    if( b_reset_reference )
    {
230
        cl->i_next_drift_update = 0;
231
        AvgReset( &cl->drift );
232 233

        /* Feed synchro with a new reference point. */
Laurent Aimar's avatar
Laurent Aimar committed
234 235 236
        cl->b_has_reference = true;
        cl->ref = clock_point_Create( i_ck_stream,
                                      __MAX( cl->i_ts_max + CR_MEAN_PTS_GAP, i_ck_system ) );
237
    }
238

239
    if( !b_can_pace_control && cl->i_next_drift_update < i_ck_system )
240
    {
241
        const mtime_t i_converted = ClockSystemToStream( cl, i_ck_system );
242

243
        AvgUpdate( &cl->drift, i_converted - i_ck_stream );
244

245
        cl->i_next_drift_update = i_ck_system + CLOCK_FREQ/5; /* FIXME why that */
246
    }
Laurent Aimar's avatar
Laurent Aimar committed
247
    cl->last = clock_point_Create( i_ck_stream, i_ck_system );
248 249

    vlc_mutex_unlock( &cl->lock );
250 251
}

Laurent Aimar's avatar
Laurent Aimar committed
252
/*****************************************************************************
Laurent Aimar's avatar
Laurent Aimar committed
253
 * input_clock_Reset:
Laurent Aimar's avatar
Laurent Aimar committed
254
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
255
void input_clock_Reset( input_clock_t *cl )
Laurent Aimar's avatar
Laurent Aimar committed
256
{
257 258
    vlc_mutex_lock( &cl->lock );

259
    cl->b_has_reference = false;
Laurent Aimar's avatar
Laurent Aimar committed
260
    cl->ref = clock_point_Create( 0, 0 );
261
    cl->i_ts_max = 0;
262 263

    vlc_mutex_unlock( &cl->lock );
Laurent Aimar's avatar
Laurent Aimar committed
264 265
}

266
/*****************************************************************************
267
 * input_clock_ChangeRate:
268
 *****************************************************************************/
269
void input_clock_ChangeRate( input_clock_t *cl, int i_rate )
270
{
271 272
    vlc_mutex_lock( &cl->lock );

273
    /* Move the reference point */
274
    if( cl->b_has_reference )
275 276
    {
        cl->last.i_system = ClockStreamToSystem( cl, cl->last.i_stream );
Laurent Aimar's avatar
Laurent Aimar committed
277
        cl->ref = cl->last;
278
    }
279

280
    cl->i_rate = i_rate;
281 282

    vlc_mutex_unlock( &cl->lock );
283 284
}

285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
/*****************************************************************************
 * input_clock_ChangePause:
 *****************************************************************************/
void input_clock_ChangePause( input_clock_t *cl, bool b_paused, mtime_t i_date )
{
    vlc_mutex_lock( &cl->lock );
    assert( (!cl->b_paused) != (!b_paused) );

    if( cl->b_paused )
    {
        const mtime_t i_duration = i_date - cl->i_pause_date;

        if( cl->b_has_reference && i_duration > 0 )
        {
            cl->ref.i_system += i_duration;
            cl->last.i_system += i_duration;
        }
    }
    cl->i_pause_date = i_date;
    cl->b_paused = b_paused;

    vlc_mutex_unlock( &cl->lock );
}

309
/*****************************************************************************
Laurent Aimar's avatar
Laurent Aimar committed
310
 * input_clock_GetWakeup
311
 *****************************************************************************/
312
mtime_t input_clock_GetWakeup( input_clock_t *cl )
313
{
314
    mtime_t i_wakeup = 0;
315

316 317 318 319 320 321 322 323 324
    vlc_mutex_lock( &cl->lock );

    /* Synchronized, we can wait */
    if( cl->b_has_reference )
        i_wakeup = ClockStreamToSystem( cl, cl->last.i_stream );

    vlc_mutex_unlock( &cl->lock );

    return i_wakeup;
325 326 327 328 329
}

/*****************************************************************************
 * input_clock_GetTS: manages a PTS or DTS
 *****************************************************************************/
330
mtime_t input_clock_GetTS( input_clock_t *cl, int *pi_rate,
331
                           mtime_t i_ts, mtime_t i_ts_bound )
332 333 334
{
    mtime_t i_converted_ts;

335 336
    vlc_mutex_lock( &cl->lock );

337 338 339
    if( pi_rate )
        *pi_rate = cl->i_rate;

340
    if( !cl->b_has_reference )
341 342
    {
        vlc_mutex_unlock( &cl->lock );
343
        return 0;
344
    }
345 346

    /* */
347 348 349 350
    i_converted_ts = ClockStreamToSystem( cl, i_ts + AvgGet( &cl->drift ) );
    if( i_converted_ts > cl->i_ts_max )
        cl->i_ts_max = i_converted_ts;

351 352
    vlc_mutex_unlock( &cl->lock );

353 354 355 356 357 358 359 360
    i_converted_ts += cl->i_pts_delay;

    /* Check ts validity */
    if( i_ts_bound != INT64_MAX &&
        i_converted_ts >= mdate() + cl->i_pts_delay + i_ts_bound )
        return 0;

    return i_converted_ts;
361
}
362 363 364 365 366 367 368 369 370 371 372 373 374
/*****************************************************************************
 * input_clock_GetRate: Return current rate
 *****************************************************************************/
int input_clock_GetRate( input_clock_t *cl )
{
    int i_rate;

    vlc_mutex_lock( &cl->lock );
    i_rate = cl->i_rate;
    vlc_mutex_unlock( &cl->lock );

    return i_rate;
}
375

376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411
int input_clock_GetState( input_clock_t *cl,
                          mtime_t *pi_stream_start, mtime_t *pi_system_start,
                          mtime_t *pi_stream_duration, mtime_t *pi_system_duration )
{
    vlc_mutex_lock( &cl->lock );

    if( !cl->b_has_reference )
    {
        vlc_mutex_unlock( &cl->lock );
        return VLC_EGENERIC;
    }

    *pi_stream_start = cl->ref.i_stream;
    *pi_system_start = cl->ref.i_system;

    *pi_stream_duration = cl->last.i_stream - cl->ref.i_stream;
    *pi_system_duration = cl->last.i_system - cl->ref.i_system;

    vlc_mutex_unlock( &cl->lock );

    return VLC_SUCCESS;
}

void input_clock_ChangeSystemOrigin( input_clock_t *cl, mtime_t i_system )
{
    vlc_mutex_lock( &cl->lock );

    assert( cl->b_has_reference );
    const mtime_t i_offset = i_system - cl->ref.i_system;

    cl->ref.i_system += i_offset;
    cl->last.i_system += i_offset;

    vlc_mutex_unlock( &cl->lock );
}

412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433
#warning "input_clock_SetJitter needs more work"
void input_clock_SetJitter( input_clock_t *cl,
                            mtime_t i_pts_delay, int i_cr_average )
{
    vlc_mutex_lock( &cl->lock );

    /* TODO always save the value, and when rebuffering use the new one if smaller
     * TODO when increasing -> force rebuffering
     */
    if( cl->i_pts_delay < i_pts_delay )
        cl->i_pts_delay = i_pts_delay;

    /* */
    if( i_cr_average < 10 )
        i_cr_average = 10;

    if( cl->drift.i_divider != i_cr_average )
        AvgRescale( &cl->drift, i_cr_average );

    vlc_mutex_unlock( &cl->lock );
}

Laurent Aimar's avatar
Laurent Aimar committed
434 435 436
/*****************************************************************************
 * ClockStreamToSystem: converts a movie clock to system date
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
437
static mtime_t ClockStreamToSystem( input_clock_t *cl, mtime_t i_stream )
Laurent Aimar's avatar
Laurent Aimar committed
438 439 440 441
{
    if( !cl->b_has_reference )
        return 0;

Laurent Aimar's avatar
Laurent Aimar committed
442
    return ( i_stream - cl->ref.i_stream ) * cl->i_rate / INPUT_RATE_DEFAULT +
Laurent Aimar's avatar
Laurent Aimar committed
443 444 445 446 447 448 449 450 451 452 453 454
           cl->ref.i_system;
}

/*****************************************************************************
 * ClockSystemToStream: converts a system date to movie clock
 *****************************************************************************
 * Caution : a valid reference point is needed for this to operate.
 *****************************************************************************/
static mtime_t ClockSystemToStream( input_clock_t *cl, mtime_t i_system )
{
    assert( cl->b_has_reference );
    return ( i_system - cl->ref.i_system ) * INPUT_RATE_DEFAULT / cl->i_rate +
Laurent Aimar's avatar
Laurent Aimar committed
455
            cl->ref.i_stream;
Laurent Aimar's avatar
Laurent Aimar committed
456 457
}

458 459 460
/*****************************************************************************
 * Long term average helpers
 *****************************************************************************/
461
static void AvgInit( average_t *p_avg, int i_divider )
462 463
{
    p_avg->i_divider = i_divider;
464
    AvgReset( p_avg );
465
}
466
static void AvgClean( average_t *p_avg )
467 468 469
{
    VLC_UNUSED(p_avg);
}
470
static void AvgReset( average_t *p_avg )
471 472 473 474 475
{
    p_avg->i_value = 0;
    p_avg->i_residue = 0;
    p_avg->i_count = 0;
}
476
static void AvgUpdate( average_t *p_avg, mtime_t i_value )
477 478 479 480 481 482 483 484 485 486 487
{
    const int i_f0 = __MIN( p_avg->i_divider - 1, p_avg->i_count );
    const int i_f1 = p_avg->i_divider - i_f0;

    const mtime_t i_tmp = i_f0 * p_avg->i_value + i_f1 * i_value + p_avg->i_residue;

    p_avg->i_value   = i_tmp / p_avg->i_divider;
    p_avg->i_residue = i_tmp % p_avg->i_divider;

    p_avg->i_count++;
}
488
static mtime_t AvgGet( average_t *p_avg )
489 490 491
{
    return p_avg->i_value;
}
492 493 494
static void AvgRescale( average_t *p_avg, int i_divider )
{
    const mtime_t i_tmp = p_avg->i_value * p_avg->i_divider + p_avg->i_residue;
Laurent Aimar's avatar
Laurent Aimar committed
495

496 497 498 499
    p_avg->i_divider = i_divider;
    p_avg->i_value   = i_tmp / p_avg->i_divider;
    p_avg->i_residue = i_tmp % p_avg->i_divider;
}