clock.c 17.6 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
/* */
#define INPUT_CLOCK_LATE_COUNT (3)

128
/* */
129 130
struct input_clock_t
{
131 132 133
    /* */
    vlc_mutex_t lock;

Laurent Aimar's avatar
Laurent Aimar committed
134
    /* Reference point */
Laurent Aimar's avatar
Laurent Aimar committed
135 136
    bool          b_has_reference;
    clock_point_t ref;
Laurent Aimar's avatar
Laurent Aimar committed
137 138 139

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

142
    /* Maximal timestamp returned by input_clock_ConvertTS (in system unit) */
143
    mtime_t i_ts_max;
144

Laurent Aimar's avatar
Laurent Aimar committed
145
    /* Clock drift */
146 147
    mtime_t i_next_drift_update;
    average_t drift;
148

149 150 151 152 153 154 155
    /* Late statistics */
    struct
    {
        mtime_t  pi_value[INPUT_CLOCK_LATE_COUNT];
        unsigned i_index;
    } late;

Laurent Aimar's avatar
Laurent Aimar committed
156 157
    /* Current modifiers */
    int     i_rate;
158
    mtime_t i_pts_delay;
159 160
    bool    b_paused;
    mtime_t i_pause_date;
161 162
};

Laurent Aimar's avatar
Laurent Aimar committed
163
static mtime_t ClockStreamToSystem( input_clock_t *, mtime_t i_stream );
Laurent Aimar's avatar
Laurent Aimar committed
164
static mtime_t ClockSystemToStream( input_clock_t *, mtime_t i_system );
165

166
/*****************************************************************************
Laurent Aimar's avatar
Laurent Aimar committed
167
 * input_clock_New: create a new clock
168
 *****************************************************************************/
169
input_clock_t *input_clock_New( int i_rate )
170
{
171 172 173 174
    input_clock_t *cl = malloc( sizeof(*cl) );
    if( !cl )
        return NULL;

175
    vlc_mutex_init( &cl->lock );
176
    cl->b_has_reference = false;
177
    cl->ref = clock_point_Create( VLC_TS_INVALID, VLC_TS_INVALID );
178

179
    cl->last = clock_point_Create( VLC_TS_INVALID, VLC_TS_INVALID );
180

181
    cl->i_ts_max = VLC_TS_INVALID;
182

183
    cl->i_next_drift_update = VLC_TS_INVALID;
184
    AvgInit( &cl->drift, 10 );
185

186 187 188 189
    cl->late.i_index = 0;
    for( int i = 0; i < INPUT_CLOCK_LATE_COUNT; i++ )
        cl->late.pi_value[i] = 0;

Laurent Aimar's avatar
Laurent Aimar committed
190
    cl->i_rate = i_rate;
191
    cl->i_pts_delay = 0;
192
    cl->b_paused = false;
193
    cl->i_pause_date = VLC_TS_INVALID;
Laurent Aimar's avatar
Laurent Aimar committed
194

195 196 197 198
    return cl;
}

/*****************************************************************************
Laurent Aimar's avatar
Laurent Aimar committed
199
 * input_clock_Delete: destroy a new clock
200
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
201
void input_clock_Delete( input_clock_t *cl )
202
{
203
    AvgClean( &cl->drift );
204
    vlc_mutex_destroy( &cl->lock );
205
    free( cl );
206 207 208
}

/*****************************************************************************
209
 * input_clock_Update: manages a clock reference
210 211 212
 *
 *  i_ck_stream: date in stream clock
 *  i_ck_system: date in system clock
213
 *****************************************************************************/
214 215 216
void input_clock_Update( input_clock_t *cl, vlc_object_t *p_log,
                         bool *pb_late,
                         bool b_can_pace_control,
Laurent Aimar's avatar
Laurent Aimar committed
217
                         mtime_t i_ck_stream, mtime_t i_ck_system )
218
{
219
    bool b_reset_reference = false;
220

221 222
    assert( i_ck_stream > VLC_TS_INVALID && i_ck_system > VLC_TS_INVALID );

223
    vlc_mutex_lock( &cl->lock );
224

225
    if( !cl->b_has_reference )
226
    {
227 228
        /* */
        b_reset_reference= true;
229
    }
230
    else if( cl->last.i_stream > VLC_TS_INVALID &&
Laurent Aimar's avatar
Laurent Aimar committed
231 232
             ( (cl->last.i_stream - i_ck_stream) > CR_MAX_GAP ||
               (cl->last.i_stream - i_ck_stream) < -CR_MAX_GAP ) )
233
    {
234 235 236
        /* Stream discontinuity, for which we haven't received a
         * warning from the stream control facilities (dd-edited
         * stream ?). */
237
        msg_Warn( p_log, "clock gap, unexpected stream discontinuity" );
238
        cl->i_ts_max = VLC_TS_INVALID;
239 240

        /* */
241
        msg_Warn( p_log, "feeding synchro with a new reference point trying to recover from clock gap" );
242 243 244 245
        b_reset_reference= true;
    }
    if( b_reset_reference )
    {
246
        cl->i_next_drift_update = VLC_TS_INVALID;
247
        AvgReset( &cl->drift );
248 249

        /* Feed synchro with a new reference point. */
Laurent Aimar's avatar
Laurent Aimar committed
250 251 252
        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 ) );
253
    }
254

255
    if( !b_can_pace_control && cl->i_next_drift_update < i_ck_system )
256
    {
257
        const mtime_t i_converted = ClockSystemToStream( cl, i_ck_system );
258

259
        AvgUpdate( &cl->drift, i_converted - i_ck_stream );
260

261
        cl->i_next_drift_update = i_ck_system + CLOCK_FREQ/5; /* FIXME why that */
262
    }
Laurent Aimar's avatar
Laurent Aimar committed
263
    cl->last = clock_point_Create( i_ck_stream, i_ck_system );
264

265 266 267
    /* It does not take the decoder latency into account but it is not really
     * the goal of the clock here */
    const mtime_t i_system_expected = ClockStreamToSystem( cl, i_ck_stream + AvgGet( &cl->drift ) );
268 269 270 271 272 273 274
    const mtime_t i_late = ( i_ck_system - cl->i_pts_delay ) - i_system_expected;
    *pb_late = i_late > 0;
    if( i_late > 0 )
    {
        cl->late.pi_value[cl->late.i_index] = i_late;
        cl->late.i_index = ( cl->late.i_index + 1 ) % INPUT_CLOCK_LATE_COUNT;
    }
275

276
    vlc_mutex_unlock( &cl->lock );
277 278
}

Laurent Aimar's avatar
Laurent Aimar committed
279
/*****************************************************************************
Laurent Aimar's avatar
Laurent Aimar committed
280
 * input_clock_Reset:
Laurent Aimar's avatar
Laurent Aimar committed
281
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
282
void input_clock_Reset( input_clock_t *cl )
Laurent Aimar's avatar
Laurent Aimar committed
283
{
284 285
    vlc_mutex_lock( &cl->lock );

286
    cl->b_has_reference = false;
287 288
    cl->ref = clock_point_Create( VLC_TS_INVALID, VLC_TS_INVALID );
    cl->i_ts_max = VLC_TS_INVALID;
289 290

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

293
/*****************************************************************************
294
 * input_clock_ChangeRate:
295
 *****************************************************************************/
296
void input_clock_ChangeRate( input_clock_t *cl, int i_rate )
297
{
298 299
    vlc_mutex_lock( &cl->lock );

300
    /* Move the reference point */
301
    if( cl->b_has_reference )
302
    {
303
        cl->last.i_system = ClockStreamToSystem( cl, cl->last.i_stream + AvgGet( &cl->drift ) );
Laurent Aimar's avatar
Laurent Aimar committed
304
        cl->ref = cl->last;
305
    }
306

307
    cl->i_rate = i_rate;
308 309

    vlc_mutex_unlock( &cl->lock );
310 311
}

312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335
/*****************************************************************************
 * 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 );
}

336
/*****************************************************************************
Laurent Aimar's avatar
Laurent Aimar committed
337
 * input_clock_GetWakeup
338
 *****************************************************************************/
339
mtime_t input_clock_GetWakeup( input_clock_t *cl )
340
{
341
    mtime_t i_wakeup = 0;
342

343 344 345 346
    vlc_mutex_lock( &cl->lock );

    /* Synchronized, we can wait */
    if( cl->b_has_reference )
347
        i_wakeup = ClockStreamToSystem( cl, cl->last.i_stream + AvgGet( &cl->drift ) );
348 349 350 351

    vlc_mutex_unlock( &cl->lock );

    return i_wakeup;
352 353 354
}

/*****************************************************************************
355
 * input_clock_ConvertTS
356
 *****************************************************************************/
357 358 359
int input_clock_ConvertTS( input_clock_t *cl,
                           int *pi_rate, mtime_t *pi_ts0, mtime_t *pi_ts1,
                           mtime_t i_ts_bound )
360
{
361
    mtime_t i_pts_delay;
362

363
    assert( pi_ts0 );
364 365
    vlc_mutex_lock( &cl->lock );

366 367 368
    if( pi_rate )
        *pi_rate = cl->i_rate;

369
    if( !cl->b_has_reference )
370 371
    {
        vlc_mutex_unlock( &cl->lock );
372
        *pi_ts0 = VLC_TS_INVALID;
373
        if( pi_ts1 )
374
            *pi_ts1 = VLC_TS_INVALID;
375
        return VLC_EGENERIC;
376
    }
377 378

    /* */
379
    if( *pi_ts0 > VLC_TS_INVALID )
380 381 382 383 384 385
    {
        *pi_ts0 = ClockStreamToSystem( cl, *pi_ts0 + AvgGet( &cl->drift ) );
        if( *pi_ts0 > cl->i_ts_max )
            cl->i_ts_max = *pi_ts0;
        *pi_ts0 += cl->i_pts_delay;
    }
386

387
    /* XXX we do not ipdate i_ts_max on purpose */
388
    if( pi_ts1 && *pi_ts1 > VLC_TS_INVALID )
389 390 391 392
    {
        *pi_ts1 = ClockStreamToSystem( cl, *pi_ts1 + AvgGet( &cl->drift ) ) +
                  cl->i_pts_delay;
    }
393

394 395
    i_pts_delay = cl->i_pts_delay;
    vlc_mutex_unlock( &cl->lock );
396 397 398

    /* Check ts validity */
    if( i_ts_bound != INT64_MAX &&
399
        *pi_ts0 > VLC_TS_INVALID && *pi_ts0 >= mdate() + i_pts_delay + i_ts_bound )
400
        return VLC_EGENERIC;
401

402
    return VLC_SUCCESS;
403
}
404 405 406 407 408 409 410 411 412 413 414 415 416
/*****************************************************************************
 * 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;
}
417

418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453
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 );
}

454 455 456 457 458 459
#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 );

460 461 462 463 464 465 466 467
    /* Update late observations */
    const mtime_t i_delay_delta = i_pts_delay - cl->i_pts_delay;
    for( int i = 0; i < INPUT_CLOCK_LATE_COUNT; i++ )
    {
        if( cl->late.pi_value[i] > 0 )
            cl->late.pi_value[i] = __MAX( cl->late.pi_value[i] - i_delay_delta, 0 );
    }

468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483
    /* 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 );
}

484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505
mtime_t input_clock_GetJitter( input_clock_t *cl )
{
    vlc_mutex_lock( &cl->lock );

#if INPUT_CLOCK_LATE_COUNT != 3
#   error "unsupported INPUT_CLOCK_LATE_COUNT"
#endif
    /* Find the median of the last late values
     * It works pretty well at rejecting bad values
     *
     * XXX we only increase pts_delay over time, decreasing it is
     * not that easy if we want to be robust.
     */
    const mtime_t *p = cl->late.pi_value;
    mtime_t i_late_median = p[0] + p[1] + p[2] - __MIN(__MIN(p[0],p[1]),p[2]) - __MAX(__MAX(p[0],p[1]),p[2]);
    mtime_t i_pts_delay = cl->i_pts_delay ;

    vlc_mutex_unlock( &cl->lock );

    return i_pts_delay + i_late_median;
}

Laurent Aimar's avatar
Laurent Aimar committed
506 507 508
/*****************************************************************************
 * ClockStreamToSystem: converts a movie clock to system date
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
509
static mtime_t ClockStreamToSystem( input_clock_t *cl, mtime_t i_stream )
Laurent Aimar's avatar
Laurent Aimar committed
510 511
{
    if( !cl->b_has_reference )
512
        return VLC_TS_INVALID;
Laurent Aimar's avatar
Laurent Aimar committed
513

Laurent Aimar's avatar
Laurent Aimar committed
514
    return ( i_stream - cl->ref.i_stream ) * cl->i_rate / INPUT_RATE_DEFAULT +
Laurent Aimar's avatar
Laurent Aimar committed
515 516 517 518 519 520 521 522 523 524 525 526
           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
527
            cl->ref.i_stream;
Laurent Aimar's avatar
Laurent Aimar committed
528 529
}

530 531 532
/*****************************************************************************
 * Long term average helpers
 *****************************************************************************/
533
static void AvgInit( average_t *p_avg, int i_divider )
534 535
{
    p_avg->i_divider = i_divider;
536
    AvgReset( p_avg );
537
}
538
static void AvgClean( average_t *p_avg )
539 540 541
{
    VLC_UNUSED(p_avg);
}
542
static void AvgReset( average_t *p_avg )
543 544 545 546 547
{
    p_avg->i_value = 0;
    p_avg->i_residue = 0;
    p_avg->i_count = 0;
}
548
static void AvgUpdate( average_t *p_avg, mtime_t i_value )
549 550 551 552 553 554 555 556 557 558 559
{
    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++;
}
560
static mtime_t AvgGet( average_t *p_avg )
561 562 563
{
    return p_avg->i_value;
}
564 565 566
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
567

568 569 570 571
    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;
}