1 /*
2 * Copyright (C) 2000-2020 the xine project
3 *
4 * This file is part of xine, a free video player.
5 *
6 * xine is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * xine is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include <time.h>
26 #include <inttypes.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <math.h>
31 #include <string.h>
32 #include <errno.h>
33 #include <pthread.h>
34 #include <sys/time.h>
35
36 #define LOG_MODULE "metronom"
37 #define LOG_VERBOSE
38 /*
39 #define LOG
40 #define LOG_AUDIO
41 */
42 #define METRONOM_CLOCK_INTERNAL
43
44 #include <xine/xine_internal.h>
45 #include <xine/metronom.h>
46 #include <xine/xineutils.h>
47 /* xine_rwlock_* */
48 #include "xine_private.h"
49
50 #define AUDIO_SAMPLE_LD 15
51 #define AUDIO_SAMPLE_NUM (1 << AUDIO_SAMPLE_LD)
52 #define AUDIO_SAMPLE_MASK (AUDIO_SAMPLE_NUM - 1)
53 #define MAX_SCR_PROVIDERS 10
54 #define MAX_SPEED_CHANGE_CALLBACKS 16
55 #define VIDEO_DRIFT_TOLERANCE 45000
56 #define AUDIO_DRIFT_TOLERANCE 45000
57
58 /* metronom video modes */
59 #define VIDEO_PREDICTION_MODE 0 /* use pts + frame duration */
60 #define VIDEO_PTS_MODE 1 /* use only pts */
61
62 /* redefine abs as macro to handle 64-bit diffs.
63 i guess llabs may not be available everywhere */
64 #define abs(x) ( ((x)<0) ? -(x) : (x) )
65
66 /*
67 * ****************************************
68 * primary SCR plugin:
69 * unix System Clock Reference
70 * ****************************************
71 */
72
73 typedef struct {
74 /* Time of last speed change. */
75 struct timeval cur_time;
76 int64_t cur_pts;
77 /* speed * 90000 / XINE_FINE_SPEED_NORMAL */
78 double speed_factor_1;
79 /* speed_factor_1 / 1000000 */
80 double speed_factor_2;
81 } unixscr_values_t;
82
83 typedef struct {
84 scr_plugin_t scr;
85 void *mem_to_free;
86 unixscr_values_t v;
87 xine_rwlock_t lock;
88 #if (HAVE_ATOMIC_VARS > 0)
89 xine_refs_t num_speed_changes;
90 #endif
91 } unixscr_t;
92
93 #if (HAVE_ATOMIC_VARS > 0)
unixscr_dummy(void * object)94 static void unixscr_dummy (void *object) {
95 (void)object;
96 }
97 #endif
98
unixscr_get_priority(scr_plugin_t * scr)99 static int unixscr_get_priority (scr_plugin_t *scr) {
100 (void)scr;
101 return 5; /* low priority */
102 }
103
104 /* Only call this when already mutex locked */
unixscr_set_pivot(unixscr_t * this)105 static void unixscr_set_pivot (unixscr_t *this) {
106 struct timeval tv;
107 double pts_calc;
108
109 xine_monotonic_clock (&tv, NULL);
110 pts_calc = (tv.tv_sec - this->v.cur_time.tv_sec) * this->v.speed_factor_1;
111 /* Make sure this diff is signed. */
112 pts_calc += ((int32_t)tv.tv_usec - (int32_t)this->v.cur_time.tv_usec) * this->v.speed_factor_2;
113 /* This next part introduces a one off inaccuracy to the scr due to rounding tv to pts. */
114 this->v.cur_pts = this->v.cur_pts + pts_calc;
115 this->v.cur_time = tv;
116 }
117
unixscr_set_speed(scr_plugin_t * scr,int speed)118 static int unixscr_set_speed (scr_plugin_t *scr, int speed) {
119 unixscr_t *this = (unixscr_t*) scr;
120
121 xine_rwlock_wrlock (&this->lock);
122 #if (HAVE_ATOMIC_VARS > 0)
123 xine_refs_add (&this->num_speed_changes, 1);
124 #endif
125
126 unixscr_set_pivot( this );
127 this->v.speed_factor_1 = (double)speed * 90000.0 / XINE_FINE_SPEED_NORMAL;
128 this->v.speed_factor_2 = this->v.speed_factor_1 * 1e-6;
129
130 #if (HAVE_ATOMIC_VARS > 0)
131 xine_refs_add (&this->num_speed_changes, 1);
132 #endif
133 xine_rwlock_unlock (&this->lock);
134
135 return speed;
136 }
137
unixscr_adjust(scr_plugin_t * scr,int64_t vpts)138 static void unixscr_adjust (scr_plugin_t *scr, int64_t vpts) {
139 unixscr_t *this = (unixscr_t*) scr;
140
141 xine_rwlock_wrlock (&this->lock);
142 #if (HAVE_ATOMIC_VARS > 0)
143 xine_refs_add (&this->num_speed_changes, 1);
144 #endif
145
146 this->v.cur_pts = vpts;
147 xine_monotonic_clock (&this->v.cur_time, NULL);
148
149 #if (HAVE_ATOMIC_VARS > 0)
150 xine_refs_add (&this->num_speed_changes, 1);
151 #endif
152 xine_rwlock_unlock (&this->lock);
153 }
154
unixscr_start(scr_plugin_t * scr,int64_t start_vpts)155 static void unixscr_start (scr_plugin_t *scr, int64_t start_vpts) {
156 unixscr_t *this = (unixscr_t*) scr;
157
158 xine_rwlock_wrlock (&this->lock);
159 #if (HAVE_ATOMIC_VARS > 0)
160 xine_refs_add (&this->num_speed_changes, 1);
161 #endif
162
163 this->v.cur_pts = start_vpts;
164 xine_monotonic_clock (&this->v.cur_time, NULL);
165 /* XINE_FINE_SPEED_NORMAL */
166 this->v.speed_factor_1 = 90000.0;
167 this->v.speed_factor_2 = 0.09;
168
169 #if (HAVE_ATOMIC_VARS > 0)
170 xine_refs_add (&this->num_speed_changes, 1);
171 #endif
172 xine_rwlock_unlock (&this->lock);
173 }
174
unixscr_get_current(scr_plugin_t * scr)175 static int64_t unixscr_get_current (scr_plugin_t *scr) {
176 unixscr_t *this = (unixscr_t*) scr;
177 struct timeval tv;
178 int64_t pts;
179
180 xine_monotonic_clock (&tv, NULL);
181 #if (HAVE_ATOMIC_VARS > 0)
182 {
183 int refs = xine_refs_get (&this->num_speed_changes);
184 if (refs & 1) {
185 /* TJ. no change in progress, try a snapshot without lock first.
186 * the "volatile" is there to stop compiler from reordering code.
187 * tested with gcc -O3 -S 4.5/x86-32 and 7/x86-64.
188 * there is a point where all optimization falls over into chaos.
189 * we are getting closer ;-) */
190 volatile unixscr_values_t v = this->v;
191
192 if (refs == xine_refs_get (&this->num_speed_changes)) {
193 double pts_calc;
194 pts_calc = (tv.tv_sec - v.cur_time.tv_sec) * v.speed_factor_1;
195 pts_calc += ((int32_t)tv.tv_usec - (int32_t)v.cur_time.tv_usec) * v.speed_factor_2;
196 pts = v.cur_pts + pts_calc;
197 return pts;
198 }
199 }
200 }
201 #endif
202 xine_rwlock_rdlock (&this->lock);
203 {
204 double pts_calc;
205 pts_calc = (tv.tv_sec - this->v.cur_time.tv_sec) * this->v.speed_factor_1;
206 pts_calc += ((int32_t)tv.tv_usec - (int32_t)this->v.cur_time.tv_usec) * this->v.speed_factor_2;
207 pts = this->v.cur_pts + pts_calc;
208 }
209 xine_rwlock_unlock (&this->lock);
210
211 return pts;
212 }
213
unixscr_exit(scr_plugin_t * scr)214 static void unixscr_exit (scr_plugin_t *scr) {
215 unixscr_t *this = (unixscr_t*) scr;
216
217 #if (HAVE_ATOMIC_VARS > 0)
218 int refs = xine_refs_get (&this->num_speed_changes);
219 xine_refs_sub (&this->num_speed_changes, refs);
220 #endif
221 xine_rwlock_destroy (&this->lock);
222 free (this->mem_to_free);
223 }
224
unixscr_init(void * this_gen)225 static scr_plugin_t *unixscr_init (void *this_gen) {
226 unixscr_t *this = (unixscr_t *)this_gen;
227
228 if (this) {
229 this->mem_to_free = NULL;
230 } else {
231 this = calloc (1, sizeof (unixscr_t));
232 if (!this)
233 return NULL;
234 this->mem_to_free = this;
235 }
236
237 this->scr.interface_version = 3;
238 this->scr.get_priority = unixscr_get_priority;
239 this->scr.set_fine_speed = unixscr_set_speed;
240 this->scr.adjust = unixscr_adjust;
241 this->scr.start = unixscr_start;
242 this->scr.get_current = unixscr_get_current;
243 this->scr.exit = unixscr_exit;
244
245 xine_rwlock_init_default (&this->lock);
246 #if (HAVE_ATOMIC_VARS > 0)
247 xine_refs_init (&this->num_speed_changes, unixscr_dummy, this);
248 #endif
249
250 this->v.cur_time.tv_sec = 0;
251 this->v.cur_time.tv_usec = 0;
252 this->v.cur_pts = 0;
253 /* XINE_SPEED_PAUSE */
254 this->v.speed_factor_1 = 0;
255 this->v.speed_factor_2 = 0;
256 lprintf("xine-scr_init complete\n");
257
258 return &this->scr;
259 }
260
261
262 /************************************************************************
263 * The master clock feature. It shall handle these basic cases: *
264 * 1. A single system clock controls all timing. *
265 * 2. Some plugin is hard wired to use its own non-adjustable clock. *
266 * That clock is slightly faster or slower than system clock. *
267 * It will drift away over time, and wants xine to follow that drift. *
268 * Such clock registers as high priority (> 5), *
269 * and thus becomes the new master. *
270 * 3. Some plugin uses its own drifting clock, but it is adjustable, *
271 * and wants xine to fix that drift. *
272 * Such clock registers as low priority (< 5). *
273 * In cases 2 and 3, we "sync" the masters time to all other clocks *
274 * roughly every 5 seconds. *
275 ************************************************************************/
276
277 /* #$@! dont break existing API */
278 typedef struct {
279 metronom_clock_t mct;
280 unixscr_t uscr;
281 int next_sync_pts; /* sync by API calls, STOP_PTS to disable */
282 enum {
283 SYNC_THREAD_NONE, /* thread disabled by user, see above */
284 SYNC_THREAD_OFF, /* no clock to sync, or thread unavailable and -"- */
285 SYNC_THREAD_RUNNING /* self explaining */
286 } sync_thread_state;
287 scr_plugin_t *providers[MAX_SCR_PROVIDERS + 1];
288 int speed_change_used;
289 xine_speed_change_cb_t *speed_change_callbacks[MAX_SPEED_CHANGE_CALLBACKS + 1];
290 void *speed_change_data[MAX_SPEED_CHANGE_CALLBACKS + 1];
291 } metronom_clock_private_t;
292
metronom_register_speed_change_callback(metronom_clock_t * this,xine_speed_change_cb_t * callback,void * user_data)293 static void metronom_register_speed_change_callback (metronom_clock_t *this,
294 xine_speed_change_cb_t *callback, void *user_data) {
295 if (callback) {
296 metronom_clock_private_t *this_priv = (metronom_clock_private_t *)this;
297 pthread_mutex_lock (&this_priv->mct.lock);
298 if (this_priv->speed_change_used < MAX_SPEED_CHANGE_CALLBACKS) {
299 this_priv->speed_change_callbacks[this_priv->speed_change_used] = callback;
300 this_priv->speed_change_data[this_priv->speed_change_used] = user_data;
301 this_priv->speed_change_used++;
302 this_priv->speed_change_callbacks[this_priv->speed_change_used] = NULL;
303 }
304 pthread_mutex_unlock (&this_priv->mct.lock);
305 }
306 }
307
metronom_unregister_speed_change_callback(metronom_clock_t * this,xine_speed_change_cb_t * callback,void * user_data)308 static void metronom_unregister_speed_change_callback (metronom_clock_t *this,
309 xine_speed_change_cb_t *callback, void *user_data) {
310 if (callback) {
311 metronom_clock_private_t *this_priv = (metronom_clock_private_t *)this;
312 int i;
313 pthread_mutex_lock (&this_priv->mct.lock);
314 for (i = 0; this_priv->speed_change_callbacks[i]; i++) {
315 if ((this_priv->speed_change_callbacks[i] == callback) &&
316 (this_priv->speed_change_data[i] == user_data)) {
317 this_priv->speed_change_used--;
318 if (i != this_priv->speed_change_used) {
319 this_priv->speed_change_callbacks[i] = this_priv->speed_change_callbacks[this_priv->speed_change_used];
320 this_priv->speed_change_data[i] = this_priv->speed_change_data[this_priv->speed_change_used];
321 }
322 this_priv->speed_change_callbacks[this_priv->speed_change_used] = NULL;
323 break;
324 }
325 }
326 pthread_mutex_unlock (&this_priv->mct.lock);
327 }
328 }
329
330 #define START_PTS 0
331 #define STOP_PTS ~0
332 #define MASK_PTS (1 << 19) /* 5.825 s */
333
metronom_start_clock(metronom_clock_t * this,int64_t pts)334 static void metronom_start_clock (metronom_clock_t *this, int64_t pts) {
335 metronom_clock_private_t *this_priv = (metronom_clock_private_t *)this;
336 scr_plugin_t **r;
337
338 lprintf("start_clock (at %" PRId64 ")\n", pts);
339
340 if (this_priv->next_sync_pts != STOP_PTS)
341 this_priv->next_sync_pts = (int)pts & MASK_PTS;
342
343 pthread_mutex_lock (&this_priv->mct.lock);
344 for (r = this_priv->providers; *r && (r < this_priv->providers + MAX_SCR_PROVIDERS); r++)
345 (*r)->start (*r, pts);
346 pthread_mutex_unlock (&this_priv->mct.lock);
347
348 this_priv->mct.speed = XINE_FINE_SPEED_NORMAL;
349 }
350
metronom_get_current_time(metronom_clock_t * this)351 static int64_t metronom_get_current_time (metronom_clock_t *this) {
352 metronom_clock_private_t *this_priv = (metronom_clock_private_t *)this;
353 int64_t pts = this_priv->mct.scr_master->get_current (this_priv->mct.scr_master);
354 scr_plugin_t **r;
355
356 /* sync not needed or done by separate thread */
357 if (((int)pts & MASK_PTS) != this_priv->next_sync_pts)
358 return pts;
359
360 this_priv->next_sync_pts ^= MASK_PTS;
361
362 pthread_mutex_lock (&this_priv->mct.lock);
363 for (r = this_priv->providers; *r && (r < this_priv->providers + MAX_SCR_PROVIDERS); r++)
364 if (*r != this_priv->mct.scr_master)
365 (*r)->adjust (*r, pts);
366 pthread_mutex_unlock (&this_priv->mct.lock);
367
368 return pts;
369 }
370
metronom_stop_clock(metronom_clock_t * this)371 static void metronom_stop_clock(metronom_clock_t *this) {
372 metronom_clock_private_t *this_priv = (metronom_clock_private_t *)this;
373 scr_plugin_t **r;
374 int i;
375
376 pthread_mutex_lock (&this_priv->mct.lock);
377 for (r = this_priv->providers; *r && (r < this_priv->providers + MAX_SCR_PROVIDERS); r++)
378 (*r)->set_fine_speed (*r, XINE_SPEED_PAUSE);
379 for (i = 0; this_priv->speed_change_callbacks[i]; i++)
380 this_priv->speed_change_callbacks[i] (this_priv->speed_change_data[i], XINE_SPEED_PAUSE);
381 pthread_mutex_unlock (&this_priv->mct.lock);
382 }
383
metronom_resume_clock(metronom_clock_t * this)384 static void metronom_resume_clock(metronom_clock_t *this) {
385 metronom_clock_private_t *this_priv = (metronom_clock_private_t *)this;
386 scr_plugin_t **r;
387 int i;
388
389 pthread_mutex_lock (&this_priv->mct.lock);
390 for (r = this_priv->providers; *r && (r < this_priv->providers + MAX_SCR_PROVIDERS); r++)
391 (*r)->set_fine_speed (*r, XINE_FINE_SPEED_NORMAL);
392 for (i = 0; this_priv->speed_change_callbacks[i]; i++)
393 this_priv->speed_change_callbacks[i] (this_priv->speed_change_data[i], XINE_FINE_SPEED_NORMAL);
394 pthread_mutex_unlock (&this_priv->mct.lock);
395 }
396
metronom_adjust_clock(metronom_clock_t * this,int64_t desired_pts)397 static void metronom_adjust_clock(metronom_clock_t *this, int64_t desired_pts) {
398 metronom_clock_private_t *this_priv = (metronom_clock_private_t *)this;
399
400 if (this_priv->mct.scr_adjustable)
401 this_priv->mct.scr_master->adjust (this_priv->mct.scr_master, desired_pts);
402 if (this_priv->next_sync_pts != STOP_PTS)
403 this_priv->next_sync_pts = (int)desired_pts & MASK_PTS;
404 }
405
metronom_set_speed(metronom_clock_t * this,int speed)406 static int metronom_set_speed (metronom_clock_t *this, int speed) {
407 metronom_clock_private_t *this_priv = (metronom_clock_private_t *)this;
408 scr_plugin_t **r;
409 int true_speed, i;
410
411 true_speed = this_priv->mct.scr_master->set_fine_speed (this_priv->mct.scr_master, speed);
412
413 this_priv->mct.speed = true_speed;
414
415 pthread_mutex_lock (&this_priv->mct.lock);
416 for (r = this_priv->providers; *r && (r < this_priv->providers + MAX_SCR_PROVIDERS); r++)
417 (*r)->set_fine_speed (*r, true_speed);
418 for (i = 0; this_priv->speed_change_callbacks[i]; i++)
419 this_priv->speed_change_callbacks[i] (this_priv->speed_change_data[i], true_speed);
420 pthread_mutex_unlock (&this_priv->mct.lock);
421
422 return true_speed;
423 }
424
425 /*
426 * metronom
427 */
428
429 typedef struct {
430
431 metronom_t metronom;
432
433 /*
434 * metronom internal stuff
435 */
436 /* general */
437 xine_t *xine;
438 metronom_t *master;
439 pthread_mutex_t lock;
440 int64_t vpts_offset;
441 int64_t prebuffer;
442
443 /* audio */
444 struct {
445 int64_t pts_per_smpls;
446 int64_t last_pts;
447 int64_t vpts;
448 int vpts_rmndr; /* the remainder for integer division */
449 int drift_step;
450 int samples;
451 int seek;
452 int force_jump;
453 int vdr_hack;
454 } audio;
455
456 /* video */
457 struct {
458 int64_t last_pts;
459 int64_t vpts;
460 int64_t av_offset;
461 int drift;
462 int drift_step;
463 int base_av_offset;
464 int force_jump;
465 int img_duration;
466 int img_cpt;
467 int mode;
468 } video;
469
470 /* subtitle */
471 struct {
472 int64_t vpts;
473 int64_t offset;
474 } spu;
475
476 /* bounce hack */
477 struct {
478 int64_t diff;
479 int64_t vpts_offs;
480 int left_audio;
481 int left_video;
482 int jumped;
483 } bounce;
484
485 /* discontinuity handling */
486 struct {
487 int have_video;
488 int have_audio;
489 int64_t last_offs;
490 int last_type;
491 int video_count;
492 int audio_count;
493 int handled_count;
494 int num_video_waiters;
495 int num_audio_waiters;
496 pthread_cond_t video_reached;
497 pthread_cond_t audio_reached;
498 } disc;
499
500 } metronom_impl_t;
501
502 /* detect vdr_xineliboutput from this sequence:
503 metronom_handle_*_discontinuity (this, DISC_STREAMSEEK, 0);
504 metronom_set_option (this, METRONOM_PREBUFFER, 2000);
505 metronom_set_option (this, METRONOM_PREBUFFER, 14400);
506 apply audio jump fix after
507 metronom_handle_*_discontinuity (this, DISC_STREAMSEEK, != 0);
508 */
509
metronom_vdr_hack_disc(metronom_impl_t * this,int64_t pts_offs)510 static void metronom_vdr_hack_disc (metronom_impl_t *this, int64_t pts_offs) {
511 if (pts_offs == 0) {
512 this->audio.vdr_hack = 0;
513 } else {
514 this->audio.seek = (this->audio.vdr_hack == 2);
515 }
516 }
517
metronom_vdr_hack_prebuffer(metronom_impl_t * this,int64_t pts)518 static void metronom_vdr_hack_prebuffer (metronom_impl_t *this, int64_t pts) {
519 if (pts == 2000) {
520 this->audio.vdr_hack = (this->audio.vdr_hack == 0) ? 1 : 0;
521 } else if (pts == 14400) {
522 this->audio.vdr_hack = (this->audio.vdr_hack == 1) || (this->audio.vdr_hack == 2) ? 2 : 0;
523 }
524 }
525
metronom_set_audio_rate(metronom_t * this_gen,int64_t pts_per_smpls)526 static void metronom_set_audio_rate (metronom_t *this_gen, int64_t pts_per_smpls) {
527 metronom_impl_t *this = (metronom_impl_t *)this_gen;
528
529 pthread_mutex_lock (&this->lock);
530
531 this->audio.pts_per_smpls = pts_per_smpls;
532
533 pthread_mutex_unlock (&this->lock);
534
535 lprintf("%" PRId64 " pts per %d samples\n", pts_per_smpls, AUDIO_SAMPLE_NUM);
536 }
537
metronom_got_spu_packet(metronom_t * this_gen,int64_t pts)538 static int64_t metronom_got_spu_packet (metronom_t *this_gen, int64_t pts) {
539 metronom_impl_t *this = (metronom_impl_t *)this_gen;
540 int64_t vpts;
541
542 pthread_mutex_lock (&this->lock);
543
544 if (this->master) {
545 this->master->set_option(this->master, METRONOM_LOCK, 1);
546
547 this->vpts_offset = this->master->get_option(this->master, METRONOM_VPTS_OFFSET | METRONOM_NO_LOCK);
548 this->spu.offset = this->master->get_option(this->master, METRONOM_SPU_OFFSET | METRONOM_NO_LOCK);
549 }
550
551 vpts = pts + this->vpts_offset + this->spu.offset;
552
553 /* no vpts going backwards please */
554 if( vpts < this->spu.vpts )
555 vpts = this->spu.vpts;
556
557 this->spu.vpts = vpts;
558
559 if (this->master) {
560 this->master->set_option(this->master, METRONOM_LOCK, 0);
561 }
562
563 pthread_mutex_unlock (&this->lock);
564 return vpts;
565 }
566
567 /* There are 3 stages of discontinuity handling:
568 * 1. At demux time (entering fifo):
569 * If pts jumps more than 2 seconds, insert a discontinuity control buf.
570 * 2. At metronom_handle_foo_discontinuity () time (leaving fifo):
571 * Wait for both fifos to reach this, then bump vpts offset.
572 * 3. At metronom_got_bar () time (after decoding):
573 * Apply vpts to output buf.
574 *
575 * Issue: Not all decoders work straight forward. Many video decoders
576 * delay and reorder their output. We could defer discontinuity handling
577 * to stage 3, but decoder may swallow frames due to errors.
578 *
579 * Issue: mpeg style cotainers may jump during reorder, and pts will
580 * bounce some 20 times. Lets detect these, and try to stay calm.
581 * Workaround: at 2, remember previous setting, and swap with it when
582 * appropriate. This reduces timeline drift. At 3, do accept a few
583 * frames from both settings.
584 *
585 * Issue: Audio frames are often small (< 500 bytes). Putting 1 such
586 * frame into mpeg-ts packets (188 bytes) will waste a lot of space.
587 * Thats why they usually take 0.5 .. 1 seconds at once, and all video
588 * frames for the same time need to be sent before. That normal
589 * discontinuity wait will then outdate some audio, and yield a gap of
590 * silence.
591 * Workaround: Use the bounce hack above to handle most absolute
592 * discontinuities without wait.
593 *
594 * Note: BOUNCE_MAX needs to be at least demux_ts:WRAP_THRESHOLD (360000).
595 * This makes sure that no jump turns into a huge gap. */
596 #define BOUNCE_MAX 360000
597
metronom_handle_discontinuity(metronom_impl_t * this,int type,int try,int64_t disc_off)598 static int metronom_handle_discontinuity (metronom_impl_t *this,
599 int type, int try, int64_t disc_off) {
600 int64_t cur_time;
601
602 /* video.vpts and audio.vpts adjustements */
603 cur_time = this->xine->clock->get_current_time(this->xine->clock);
604
605 switch (type) {
606 /* When switching streams gaplessly, a paradox situation may happen:
607 * Engine was very fast and filled output buffers with more than
608 * this->prebuffer of yet to be played frames from the end of previous
609 * stream. The DISC_STREAMSTART code below will then set back vpts
610 * a few frames, and the engine will drop them later.
611 * We could try to fix this by increasing this->prebuffer, but that
612 * would cumulate over large playlists, and finally blow out queue
613 * sizes.
614 * Instead, we wait here a bit.
615 */
616 case DISC_GAPLESS:
617 {
618 int64_t t;
619 int speed = this->xine->clock->speed;
620 if (speed <= 0)
621 return 0;
622 pthread_mutex_lock (&this->lock);
623 t = this->video.vpts > this->audio.vpts ? this->video.vpts : this->audio.vpts;
624 t -= this->prebuffer + cur_time;
625 pthread_mutex_unlock (&this->lock);
626 if ((t <= 0) || (t > 135000))
627 return 0;
628 xprintf (this->xine, XINE_VERBOSITY_DEBUG,
629 "metronom: gapless switch: wait %u pts.\n", (unsigned int)t);
630 /* XINE_FINE_SPEED_NORMAL == 1000000; 1000000 * 1000 / 90 == 100000000 / 9; */
631 xine_usec_sleep (xine_uint_mul_div (t, 100000000, speed * 9));
632 }
633 return 0;
634
635 case DISC_STREAMSTART:
636 lprintf ("DISC_STREAMSTART\n");
637 disc_off = 0;
638 /* fall through */
639 case DISC_STREAMSEEK:
640 lprintf ("DISC_STREAMSEEK\n");
641 this->video.vpts = this->prebuffer + cur_time;
642 this->audio.vpts = this->video.vpts;
643 this->vpts_offset = this->video.vpts - disc_off;
644 this->bounce.left_audio = -1;
645 this->bounce.left_video = -1;
646 this->bounce.jumped = 0;
647 this->audio.vpts_rmndr = 0;
648 this->audio.force_jump = 1;
649 this->video.force_jump = 1;
650 this->video.drift = 0;
651 this->video.last_pts = 0;
652 this->audio.last_pts = 0;
653 metronom_vdr_hack_disc (this, disc_off);
654 xprintf (this->xine, XINE_VERBOSITY_DEBUG,
655 "metronom: vpts adjusted with prebuffer to %" PRId64 ".\n", this->video.vpts);
656 lprintf("video.vpts: %" PRId64 ", audio.vpts: %" PRId64 "\n", this->video.vpts, this->audio.vpts);
657 return 0;
658
659 case DISC_ABSOLUTE: {
660 int64_t d, video_vpts, vpts_offset;
661 int mode;
662 lprintf ("DISC_ABSOLUTE\n");
663 this->audio.seek = 0;
664 /* calculate but dont set yet */
665 mode = ((this->video.vpts < cur_time) << 1) | (this->audio.vpts < cur_time);
666 video_vpts = (mode == 3) ? this->prebuffer + cur_time
667 : (mode == 2) ? this->audio.vpts
668 : this->video.vpts;
669 vpts_offset = video_vpts - disc_off;
670 /* where are we? */
671 d = vpts_offset - this->vpts_offset;
672 if (d < 0)
673 d = -d;
674 if (d < BOUNCE_MAX) {
675 /* small step, keep old previous. */
676 ;
677 } else {
678 /* big step. */
679 d = vpts_offset - this->bounce.vpts_offs;
680 if (d < 0)
681 d = -d;
682 if (d < BOUNCE_MAX) {
683 /* near old previous, swap with it. */
684 d = this->vpts_offset;
685 this->vpts_offset = this->bounce.vpts_offs;
686 this->bounce.vpts_offs = d;
687 d -= this->vpts_offset;
688 this->bounce.diff = d;
689 this->bounce.left_audio = BOUNCE_MAX;
690 this->bounce.left_video = BOUNCE_MAX;
691 this->audio.last_pts = 0;
692 this->video.last_pts = 0;
693 xprintf (this->xine, XINE_VERBOSITY_DEBUG, "metronom: pts bounce by %" PRId64 ".\n", d);
694 return 0;
695 }
696 if (try && (this->bounce.left_audio >= 0))
697 return 1;
698 /* remember current as prev, and set new. */
699 this->bounce.vpts_offs = this->vpts_offset;
700 }
701 this->vpts_offset = vpts_offset;
702 this->bounce.diff = this->bounce.vpts_offs - vpts_offset;
703 this->video.vpts = video_vpts;
704 this->bounce.left_audio = BOUNCE_MAX;
705 this->bounce.left_video = BOUNCE_MAX;
706 this->bounce.jumped = 1;
707 if (mode == 2) {
708 /* still frame with audio */
709 xprintf (this->xine, XINE_VERBOSITY_DEBUG,
710 "metronom: video vpts adjusted to audio vpts %" PRId64 ".\n", this->video.vpts);
711 } else if (mode == 3) {
712 /* still frame, no audio */
713 this->audio.vpts = video_vpts;
714 this->audio.vpts_rmndr = 0;
715 this->video.force_jump = 1;
716 this->audio.force_jump = 1;
717 this->video.drift = 0;
718 xprintf (this->xine, XINE_VERBOSITY_DEBUG,
719 "metronom: vpts adjusted with prebuffer to %" PRId64 ".\n", this->video.vpts);
720 } else if (mode == 1) {
721 /* video, no sound */
722 this->audio.vpts = video_vpts;
723 this->audio.vpts_rmndr = 0;
724 xprintf (this->xine, XINE_VERBOSITY_DEBUG,
725 "metronom: audio vpts adjusted to video vpts %" PRId64 ".\n", this->video.vpts);
726 }
727 }
728 this->video.last_pts = 0;
729 this->audio.last_pts = 0;
730 lprintf ("video.vpts: %" PRId64 ", audio.vpts: %" PRId64 "\n", this->video.vpts, this->audio.vpts);
731 return 0;
732
733 case DISC_RELATIVE:
734 lprintf ("DISC_RELATIVE\n");
735 if (this->video.vpts < cur_time) {
736 /* still frame */
737 if (this->audio.vpts > cur_time) {
738 /* still frame with audio */
739 this->video.vpts = this->audio.vpts;
740 xprintf (this->xine, XINE_VERBOSITY_DEBUG,
741 "metronom: video vpts adjusted to audio vpts %" PRId64 ".\n", this->video.vpts);
742 } else {
743 /* still frame, no audio */
744 this->video.vpts = this->prebuffer + cur_time;
745 this->audio.vpts = this->video.vpts;
746 this->audio.vpts_rmndr = 0;
747 this->video.force_jump = 1;
748 this->audio.force_jump = 1;
749 this->video.drift = 0;
750 xprintf (this->xine, XINE_VERBOSITY_DEBUG,
751 "metronom: vpts adjusted with prebuffer to %" PRId64 ".\n", this->video.vpts);
752 }
753 } else {
754 /* video */
755 if (this->audio.vpts < cur_time) {
756 /* video, no sound */
757 this->audio.vpts = this->video.vpts;
758 this->audio.vpts_rmndr = 0;
759 xprintf (this->xine, XINE_VERBOSITY_DEBUG,
760 "metronom: audio vpts adjusted to video vpts %" PRId64 ".\n", this->video.vpts);
761 } else {
762 /* video + audio */
763 }
764 }
765 this->vpts_offset = this->vpts_offset - disc_off;
766 this->video.last_pts = 0;
767 this->audio.last_pts = 0;
768 lprintf ("video.vpts: %" PRId64 ", audio.vpts: %" PRId64 "\n", this->video.vpts, this->audio.vpts);
769 return 0;
770
771 default:
772 return 0;
773 }
774 }
775
metronom_handle_vdr_trick_pts(metronom_impl_t * this,int64_t pts)776 static void metronom_handle_vdr_trick_pts (metronom_impl_t *this, int64_t pts) {
777 int64_t cur_time = this->xine->clock->get_current_time (this->xine->clock);
778 if (this->video.vpts < cur_time) {
779 if (this->audio.vpts >= cur_time) {
780 /* still frame with audio */
781 this->video.vpts = this->audio.vpts;
782 } else {
783 /* still frame, no audio */
784 this->audio.vpts =
785 this->video.vpts = this->prebuffer + cur_time;
786 this->audio.vpts_rmndr = 0;
787 this->video.force_jump = 1;
788 this->audio.force_jump = 1;
789 this->video.drift = 0;
790 }
791 } else {
792 if (this->audio.vpts < cur_time) {
793 /* video, no sound */
794 this->audio.vpts = this->video.vpts;
795 this->audio.vpts_rmndr = 0;
796 }
797 }
798 this->vpts_offset = this->video.vpts - pts;
799 this->bounce.diff = this->bounce.vpts_offs - this->vpts_offset;
800 this->bounce.left_audio = -1;
801 this->bounce.left_video = -1;
802 this->bounce.jumped = 0;
803 xprintf (this->xine, XINE_VERBOSITY_DEBUG,
804 "metronom: vdr trick pts %" PRId64 ", vpts %" PRId64 ".\n", pts, this->video.vpts);
805 }
806
metronom_handle_video_discontinuity(metronom_t * this_gen,int type,int64_t disc_off)807 static void metronom_handle_video_discontinuity (metronom_t *this_gen, int type,
808 int64_t disc_off) {
809 metronom_impl_t *this = (metronom_impl_t *)this_gen;
810 int waited;
811
812 if (type == DISC_GAPLESS) {
813 /* this would cause deadlock in metronom_handle_discontinuity()
814 because of double pthread_mutex_lock(&this->lock) */
815 _x_assert(type != DISC_GAPLESS);
816 return;
817 }
818
819 pthread_mutex_lock (&this->lock);
820
821 if (this->master) {
822 /* slaves are currently not allowed to set discontinuities */
823 pthread_mutex_unlock(&this->lock);
824 return;
825 }
826
827 this->disc.video_count++;
828 if (this->disc.num_video_waiters && (this->disc.audio_count <= this->disc.video_count))
829 pthread_cond_signal (&this->disc.video_reached);
830
831 xprintf (this->xine, XINE_VERBOSITY_DEBUG,
832 "metronom: video discontinuity #%d, type is %d, disc_off %" PRId64 ".\n",
833 this->disc.video_count, type, disc_off);
834
835 if (this->disc.video_count <= this->disc.handled_count) {
836 pthread_mutex_unlock (&this->lock);
837 return;
838 }
839
840 if (type == DISC_ABSOLUTE) {
841 if (!metronom_handle_discontinuity (this, type, 1, disc_off)) {
842 this->disc.handled_count = this->disc.video_count;
843 pthread_mutex_unlock (&this->lock);
844 return;
845 }
846 }
847
848 /* If both audio and video are there, the video side shall take
849 * effect. Previous code did this by letting audio wait even if
850 * video came first. Lets drop that unnecessary wait, and pass
851 * over params instead. */
852 this->disc.last_type = type;
853 this->disc.last_offs = disc_off;
854
855 waited = 0;
856 if (this->disc.have_audio) {
857 while (this->disc.audio_count <
858 this->disc.video_count) {
859
860 xprintf (this->xine, XINE_VERBOSITY_DEBUG,
861 "metronom: waiting for audio discontinuity #%d...\n",
862 this->disc.video_count);
863
864 this->disc.num_audio_waiters++;
865 pthread_cond_wait (&this->disc.audio_reached, &this->lock);
866 this->disc.num_audio_waiters--;
867 waited = 1;
868 }
869 }
870
871 if (!waited) {
872 metronom_handle_discontinuity (this, type, 0, disc_off);
873 this->disc.handled_count++;
874 }
875
876 pthread_mutex_unlock (&this->lock);
877 }
878
metronom_got_video_frame(metronom_t * this_gen,vo_frame_t * img)879 static void metronom_got_video_frame (metronom_t *this_gen, vo_frame_t *img) {
880
881 metronom_impl_t *this = (metronom_impl_t *)this_gen;
882 int64_t pts = img->pts;
883
884 pthread_mutex_lock (&this->lock);
885
886 if (this->master) {
887 this->master->set_option(this->master, METRONOM_LOCK, 1);
888
889 if (!this->disc.handled_count) {
890 /* we are not initialized yet */
891
892 this->video.vpts = this->audio.vpts = this->master->get_option(this->master, METRONOM_VPTS | METRONOM_NO_LOCK);
893
894 /* when being attached to the first master, do not drift into
895 * his vpts values but adopt at once */
896 this->audio.force_jump = 1;
897 this->video.force_jump = 1;
898 this->disc.handled_count++;
899 }
900
901 this->vpts_offset = this->master->get_option(this->master, METRONOM_VPTS_OFFSET | METRONOM_NO_LOCK);
902 this->video.av_offset = this->master->get_option(this->master, METRONOM_AV_OFFSET | METRONOM_NO_LOCK);
903 }
904
905 lprintf("got_video_frame pts = %" PRId64 ", duration = %d\n", pts, img->duration);
906
907 this->video.img_cpt++;
908
909 /* 1000 fps usually means unknown or variable frame rate */
910 if (img->duration > 90) {
911 this->video.mode = VIDEO_PREDICTION_MODE;
912 this->video.img_duration = img->duration;
913 } else {
914 /* will skip the whole predicted vpts stuff */
915 this->video.mode = VIDEO_PTS_MODE;
916 }
917
918 /* goom likes to deliver all zero pts sometimes. Give a chance to follow
919 at least sound card drift */
920 if (!pts && img->duration && !(this->video.img_cpt & 0x7f))
921 pts = this->video.last_pts + this->video.img_cpt * img->duration;
922
923 if (pts && pts != this->video.last_pts) {
924
925 if (!img->duration) {
926 /* Compute the duration of previous frames using this formula:
927 * duration = (curent_pts - last_pts) / (frame count between the 2 pts)
928 * This duration will be used to predict the next frame vpts.
929 */
930 if (this->video.last_pts && this->video.img_cpt) {
931 this->video.img_duration = (pts - this->video.last_pts) / this->video.img_cpt;
932 lprintf("computed frame_duration = %d\n", this->video.img_duration );
933 }
934 }
935 this->video.img_cpt = 0;
936 this->video.last_pts = pts;
937
938
939 /*
940 * compare predicted (this->video.vpts) and given (pts+vpts_offset)
941 * pts values - hopefully they will be the same
942 * if not, for small diffs try to interpolate
943 * for big diffs: jump
944 */
945
946 pts += this->vpts_offset;
947
948 if (this->bounce.left_video >= 0) {
949 int64_t diff = this->video.vpts - pts;
950 if ((abs (diff) > BOUNCE_MAX) && (abs (diff - this->bounce.diff) < BOUNCE_MAX)) {
951 pts += this->bounce.diff;
952 xprintf (this->xine, XINE_VERBOSITY_DEBUG, "metronom: bounced video frame with pts %" PRId64 ".\n", img->pts);
953 }
954 this->bounce.left_video -= img->duration;
955 if (this->bounce.left_video < 0) {
956 this->bounce.left_audio = -1;
957 this->bounce.left_video = -1;
958 xprintf (this->xine, XINE_VERBOSITY_DEBUG,
959 "metronom: leaving bounce area at pts %" PRId64 ".\n", img->pts);
960 }
961 }
962
963 if (this->video.mode == VIDEO_PREDICTION_MODE) {
964
965 int64_t diff = this->video.vpts - pts;
966
967 lprintf("video diff is %" PRId64 " (predicted %" PRId64 ", given %" PRId64 ")\n", diff, this->video.vpts, pts);
968
969 if ((abs (diff) > VIDEO_DRIFT_TOLERANCE) || (this->video.force_jump)) {
970
971
972 xprintf (this->xine, XINE_VERBOSITY_DEBUG, "metronom: video jump by %"PRId64" pts.\n", -diff);
973 this->video.force_jump = 0;
974 this->video.vpts = pts;
975 this->video.drift = 0;
976 this->video.drift_step = 0;
977
978 } else {
979 /* TJ. Drift into new value over the next 32 frames.
980 * Dont fall into the asymptote trap of bringing down step with remaining drift.
981 * BTW. video.drift* merely uses 17 bits.
982 */
983 this->video.drift = diff;
984 if (diff < 0) {
985 int step = ((int)diff - 31) >> 5;
986 if (this->video.drift_step > step)
987 this->video.drift_step = step;
988 else if (this->video.drift_step < (int)diff)
989 this->video.drift_step = diff;
990 } else {
991 int step = ((int)diff + 31) >> 5;
992 if (this->video.drift_step < step)
993 this->video.drift_step = step;
994 else if (this->video.drift_step > (int)diff)
995 this->video.drift_step = diff;
996 }
997 }
998 } else {
999 /* VIDEO_PTS_MODE: do not use the predicted value */
1000 this->video.vpts = pts;
1001 this->video.drift = 0;
1002 this->video.drift_step = 0;
1003 }
1004 }
1005
1006 img->vpts = this->video.vpts + this->video.av_offset + this->video.base_av_offset;
1007
1008 /* We need to update this->video.vpts is both modes.
1009 * this->video.vpts is used as the next frame vpts if next frame pts=0
1010 */
1011 this->video.vpts += this->video.img_duration - this->video.drift_step;
1012
1013 if (this->video.mode == VIDEO_PREDICTION_MODE) {
1014 lprintf("video vpts for %10"PRId64" : %10"PRId64" (duration:%d drift:%d step:%d)\n",
1015 img->pts, this->video.vpts, img->duration, this->video.drift, this->video.drift_step );
1016
1017 /* reset drift compensation if work is done after this frame */
1018 if (this->video.drift_step < 0) {
1019 this->video.drift -= this->video.drift_step;
1020 if (this->video.drift >= 0) {
1021 this->video.drift = 0;
1022 this->video.drift_step = 0;
1023 }
1024 } else if (this->video.drift_step > 0) {
1025 this->video.drift -= this->video.drift_step;
1026 if (this->video.drift <= 0) {
1027 this->video.drift = 0;
1028 this->video.drift_step = 0;
1029 }
1030 }
1031 }
1032
1033
1034 if (this->master) {
1035 this->master->set_option(this->master, METRONOM_LOCK, 0);
1036 }
1037
1038 pthread_mutex_unlock (&this->lock);
1039 if (this->xine->verbosity == XINE_VERBOSITY_DEBUG + 1)
1040 xprintf (this->xine, XINE_VERBOSITY_DEBUG + 1, "metronom: video pts: %"PRId64":%04d -> %"PRId64".\n",
1041 img->pts, (int)img->duration, img->vpts);
1042 }
1043
metronom_handle_audio_discontinuity(metronom_t * this_gen,int type,int64_t disc_off)1044 static void metronom_handle_audio_discontinuity (metronom_t *this_gen, int type,
1045 int64_t disc_off) {
1046 metronom_impl_t *this = (metronom_impl_t *)this_gen;
1047 int waited;
1048
1049 if (type == DISC_GAPLESS) {
1050 metronom_handle_discontinuity (this, type, 0, disc_off);
1051 return;
1052 }
1053
1054 pthread_mutex_lock (&this->lock);
1055
1056 if (this->master) {
1057 /* slaves are currently not allowed to set discontinuities */
1058 pthread_mutex_unlock(&this->lock);
1059 return;
1060 }
1061
1062 this->disc.audio_count++;
1063 if (this->disc.num_audio_waiters && (this->disc.audio_count >= this->disc.video_count))
1064 pthread_cond_signal (&this->disc.audio_reached);
1065
1066 xprintf (this->xine, XINE_VERBOSITY_DEBUG,
1067 "metronom: audio discontinuity #%d, type is %d, disc_off %" PRId64 ".\n",
1068 this->disc.audio_count, type, disc_off);
1069
1070 if (this->disc.audio_count <= this->disc.handled_count) {
1071 pthread_mutex_unlock (&this->lock);
1072 return;
1073 }
1074
1075 if (type == DISC_ABSOLUTE) {
1076 if (!metronom_handle_discontinuity (this, type, 1, disc_off)) {
1077 this->disc.handled_count = this->disc.audio_count;
1078 pthread_mutex_unlock (&this->lock);
1079 return;
1080 }
1081 }
1082
1083 waited = 0;
1084 if (this->disc.have_video) {
1085 while ( this->disc.audio_count >
1086 this->disc.video_count ) {
1087
1088 xprintf (this->xine, XINE_VERBOSITY_DEBUG,
1089 "metronom: waiting for video discontinuity #%d...\n",
1090 this->disc.audio_count);
1091
1092 this->disc.num_video_waiters++;
1093 pthread_cond_wait (&this->disc.video_reached, &this->lock);
1094 this->disc.num_video_waiters--;
1095 waited = 1;
1096 }
1097 } else {
1098 this->disc.last_type = type;
1099 this->disc.last_offs = disc_off;
1100 }
1101
1102 if (!waited) {
1103 metronom_handle_discontinuity (this, this->disc.last_type, 0, this->disc.last_offs);
1104 this->disc.handled_count++;
1105 }
1106 this->audio.samples = 0;
1107 this->audio.drift_step = 0;
1108
1109 pthread_mutex_unlock (&this->lock);
1110 }
1111
metronom_got_audio_samples(metronom_t * this_gen,int64_t pts,int nsamples)1112 static int64_t metronom_got_audio_samples (metronom_t *this_gen, int64_t pts,
1113 int nsamples) {
1114
1115 metronom_impl_t *this = (metronom_impl_t *)this_gen;
1116 int64_t vpts;
1117
1118 lprintf("got %d audio samples, pts is %" PRId64 ", last pts = %" PRId64 "\n", nsamples, pts, this->audio.last_pts);
1119 lprintf("AUDIO pts from last= %" PRId64 "\n", pts-this->audio.last_pts);
1120
1121 pthread_mutex_lock (&this->lock);
1122
1123 if (this->master) {
1124 this->master->set_option(this->master, METRONOM_LOCK, 1);
1125
1126 if (!this->disc.handled_count) {
1127 /* we are not initialized yet */
1128
1129 this->video.vpts = this->audio.vpts = this->master->get_option(this->master, METRONOM_VPTS | METRONOM_NO_LOCK);
1130
1131 this->audio.vpts_rmndr = 0;
1132 /* when being attached to the first master, do not drift into
1133 * his vpts values but adopt at once */
1134 this->audio.force_jump = 1;
1135 this->video.force_jump = 1;
1136 this->disc.handled_count++;
1137 }
1138
1139 this->vpts_offset = this->master->get_option(this->master, METRONOM_VPTS_OFFSET | METRONOM_NO_LOCK);
1140 }
1141
1142 if (pts && pts != this->audio.last_pts) {
1143 int64_t diff;
1144 this->audio.last_pts = pts;
1145 vpts = pts + this->vpts_offset;
1146 diff = this->audio.vpts - vpts;
1147
1148 /* Attempt to fix that mpeg-ts "video ahead of audio" issue with vdr-libxineoutput. */
1149 if (this->audio.seek) {
1150 this->audio.seek = 0;
1151 if ((diff > 0) && (diff < 220000)) {
1152 vpts += diff;
1153 this->vpts_offset += diff;
1154 xprintf (this->xine, XINE_VERBOSITY_DEBUG,
1155 "metronom: fixing seek jump by %d pts.\n", (int)diff);
1156 diff = 0;
1157 }
1158 }
1159
1160 if (this->bounce.left_audio >= 0) {
1161 if ((abs (diff) > BOUNCE_MAX) && (abs (diff - this->bounce.diff) < BOUNCE_MAX)) {
1162 vpts += this->bounce.diff;
1163 diff = this->audio.vpts - vpts;
1164 xprintf (this->xine, XINE_VERBOSITY_DEBUG, "metronom: bounced audio buffer with pts %" PRId64 ".\n", pts);
1165 }
1166 this->bounce.left_audio -= (nsamples * this->audio.pts_per_smpls) >> AUDIO_SAMPLE_LD;
1167 if (this->bounce.left_audio < 0) {
1168 this->bounce.left_audio = -1;
1169 this->bounce.left_video = -1;
1170 xprintf (this->xine, XINE_VERBOSITY_DEBUG,
1171 "metronom: leaving bounce area at pts %" PRId64 ".\n", pts);
1172 }
1173 if (this->bounce.jumped) {
1174 if ((diff > 0) /* && (diff < BOUNCE_MAX) */) {
1175 vpts += diff;
1176 this->vpts_offset += diff;
1177 this->bounce.diff = this->bounce.vpts_offs - this->vpts_offset;
1178 xprintf (this->xine, XINE_VERBOSITY_DEBUG,
1179 "metronom: fixing discontinuity jump by %" PRId64 " pts.\n", diff);
1180 diff = 0;
1181 }
1182 }
1183 }
1184
1185 /* compare predicted and given vpts */
1186 if((abs(diff) > AUDIO_DRIFT_TOLERANCE) || (this->audio.force_jump)) {
1187 xprintf (this->xine, XINE_VERBOSITY_DEBUG, "metronom: audio jump by %" PRId64 " pts.\n", -diff);
1188 this->audio.force_jump = 0;
1189 this->audio.vpts = vpts;
1190 this->audio.vpts_rmndr = 0;
1191 this->audio.drift_step = 0;
1192 } else {
1193 if( this->audio.samples ) {
1194 /* calculate drift_step to recover vpts errors */
1195 int d = diff, m;
1196 lprintf ("audio diff = %d\n", d);
1197
1198 d *= AUDIO_SAMPLE_NUM / 4;
1199 d /= (int)this->audio.samples;
1200 /* drift_step is not allowed to change rate by more than 25% */
1201 m = (int)this->audio.pts_per_smpls >> 2;
1202 if (d > m) d = m;
1203 else if (d < -m) d = -m;
1204 this->audio.drift_step = d;
1205
1206 lprintf ("audio_drift = %d, audio.pts_per_smpls = %" PRId64 "\n", d, this->audio.pts_per_smpls);
1207 }
1208 }
1209 this->audio.samples = 0;
1210 }
1211 vpts = this->audio.vpts;
1212
1213 /* drift here is caused by streams where nominal sample rate differs from
1214 * the rate of which pts increments. fixing the audio.vpts won't do us any
1215 * good because sound card won't play it faster or slower just because
1216 * we want. however, adding the error to the vpts_offset will force video
1217 * to change it's frame rate to keep in sync with us.
1218 *
1219 * Since we are using integer division below, it can happen that we lose
1220 * precision for the calculated duration in vpts for each audio buffer
1221 * (< 1 PTS, e.g. 0.25 PTS during playback of most DVDs with LPCM audio).
1222 * This would lead to a situation where the sound card actually needs
1223 * more time to play back the buffers, than the audio buffer's vpts field
1224 * indicates. This makes audio_out loop think we are in sync with the
1225 * soundcard, while we actually are not. So that's why there is the extra
1226 * modulo calculation, to keep track of the truncated, fractional part.
1227 * However, this is but a nice try after all because
1228 * 1. "pts per 2^15 samples" itself has a similar size rounding error.
1229 * 2. System and sound card clocks are unprecise independently.
1230 * 3. System clock is not an exact multiple of sample rate.
1231 * So let audio out help us fixing this through occasional feedback there :-)
1232 */
1233 this->audio.vpts_rmndr += (nsamples * this->audio.pts_per_smpls) & AUDIO_SAMPLE_MASK;
1234 this->audio.vpts += (nsamples * this->audio.pts_per_smpls) >> AUDIO_SAMPLE_LD;
1235 if (this->audio.vpts_rmndr >= AUDIO_SAMPLE_NUM) {
1236 this->audio.vpts += 1;
1237 this->audio.vpts_rmndr -= AUDIO_SAMPLE_NUM;
1238 }
1239 this->audio.samples += nsamples;
1240 this->vpts_offset += (nsamples * this->audio.drift_step) >> AUDIO_SAMPLE_LD;
1241
1242 if (this->master) {
1243 this->master->set_option(this->master, METRONOM_LOCK, 0);
1244 }
1245 pthread_mutex_unlock (&this->lock);
1246
1247 lprintf ("audio vpts for %10"PRId64" : %10"PRId64"\n", pts, vpts);
1248 return vpts;
1249 }
1250
metronom_set_option(metronom_t * this_gen,int option,int64_t value)1251 static void metronom_set_option (metronom_t *this_gen, int option, int64_t value) {
1252
1253 metronom_impl_t *this = (metronom_impl_t *)this_gen;
1254
1255 if (option == METRONOM_LOCK) {
1256 if (value) {
1257 pthread_mutex_lock (&this->lock);
1258 if (this->master)
1259 this->master->set_option(this->master, option, value);
1260 } else {
1261 if (this->master)
1262 this->master->set_option(this->master, option, value);
1263 pthread_mutex_unlock (&this->lock);
1264 }
1265 return;
1266 }
1267
1268 pthread_mutex_lock (&this->lock);
1269
1270 if (this->master) {
1271 /* pass the option on to the master */
1272 this->master->set_option(this->master, option, value);
1273 pthread_mutex_unlock(&this->lock);
1274 return;
1275 }
1276
1277 switch (option) {
1278 case METRONOM_AV_OFFSET:
1279 this->video.av_offset = value;
1280 xprintf (this->xine, XINE_VERBOSITY_LOG,
1281 "metronom: video.av_offset=%" PRId64 " pts.\n", this->video.av_offset);
1282 break;
1283 case METRONOM_SPU_OFFSET:
1284 this->spu.offset = value;
1285 xprintf (this->xine, XINE_VERBOSITY_LOG,
1286 "metronom: spu.offset=%" PRId64 " pts.\n", this->spu.offset);
1287 break;
1288 case METRONOM_ADJ_VPTS_OFFSET:
1289 this->audio.vpts += value;
1290 this->audio.vpts_rmndr = 0;
1291
1292 /* that message should be rare, please report otherwise.
1293 * when xine is in some sort of "steady state" hearing it
1294 * once in a while means a small sound card drift (or system
1295 * clock drift -- who knows?). nothing to worry about.
1296 */
1297 xprintf (this->xine, XINE_VERBOSITY_LOG,
1298 "metronom: fixing sound card drift by %" PRId64 " pts.\n", value);
1299 break;
1300 case METRONOM_PREBUFFER:
1301 this->prebuffer = value;
1302 metronom_vdr_hack_prebuffer (this, value);
1303 xprintf (this->xine, XINE_VERBOSITY_LOG,
1304 "metronom: prebuffer=%" PRId64 " pts.\n", this->prebuffer);
1305 break;
1306 case METRONOM_VDR_TRICK_PTS:
1307 metronom_handle_vdr_trick_pts (this, value);
1308 break;
1309 default:
1310 xprintf(this->xine, XINE_VERBOSITY_NONE,
1311 "metronom: unknown option in set_option: %d.\n", option);
1312 }
1313
1314 pthread_mutex_unlock (&this->lock);
1315 }
1316
metronom_clock_set_option(metronom_clock_t * this,int option,int64_t value)1317 static void metronom_clock_set_option (metronom_clock_t *this,
1318 int option, int64_t value) {
1319
1320 pthread_mutex_lock (&this->lock);
1321
1322 switch (option) {
1323 case CLOCK_SCR_ADJUSTABLE:
1324 this->scr_adjustable = value;
1325 break;
1326 default:
1327 xprintf (this->xine, XINE_VERBOSITY_NONE,
1328 "metronom: unknown option in set_option: %d.\n", option);
1329 }
1330
1331 pthread_mutex_unlock (&this->lock);
1332 }
1333
metronom_get_option(metronom_t * this_gen,int option)1334 static int64_t metronom_get_option (metronom_t *this_gen, int option) {
1335
1336 metronom_impl_t *this = (metronom_impl_t *)this_gen;
1337 int64_t result;
1338 int mutex_locked;
1339
1340 if (option & METRONOM_NO_LOCK) {
1341 mutex_locked = 0;
1342 } else {
1343 pthread_mutex_lock (&this->lock);
1344 mutex_locked = 1;
1345 }
1346
1347 if (this->master) {
1348 result = this->master->get_option(this->master, option);
1349 if (mutex_locked)
1350 pthread_mutex_unlock (&this->lock);
1351 return result;
1352 }
1353
1354 option &= ~METRONOM_NO_LOCK;
1355
1356 switch (option) {
1357 case METRONOM_AV_OFFSET:
1358 result = this->video.av_offset;
1359 break;
1360 case METRONOM_SPU_OFFSET:
1361 result = this->spu.offset;
1362 break;
1363 case METRONOM_FRAME_DURATION:
1364 result = this->video.img_duration;
1365 break;
1366 case METRONOM_VPTS_OFFSET:
1367 result = this->vpts_offset;
1368 break;
1369 case METRONOM_PREBUFFER:
1370 result = this->prebuffer;
1371 break;
1372 case METRONOM_VPTS:
1373 if (this->video.vpts > this->audio.vpts)
1374 result = this->video.vpts;
1375 else
1376 result = this->audio.vpts;
1377 break;
1378 case METRONOM_WAITING:
1379 result = (this->disc.num_audio_waiters ? 1 : 0) | (this->disc.num_video_waiters ? 2 : 0);
1380 break;
1381 case METRONOM_VDR_TRICK_PTS:
1382 result = this->video.vpts;
1383 break;
1384 default:
1385 result = 0;
1386 xprintf (this->xine, XINE_VERBOSITY_NONE,
1387 "metronom: unknown option in get_option: %d.\n", option);
1388 break;
1389 }
1390
1391 if (mutex_locked) {
1392 pthread_mutex_unlock (&this->lock);
1393 }
1394
1395 return result;
1396 }
1397
metronom_clock_get_option(metronom_clock_t * this,int option)1398 static int64_t metronom_clock_get_option (metronom_clock_t *this, int option) {
1399 switch (option) {
1400 case CLOCK_SCR_ADJUSTABLE:
1401 return this->scr_adjustable;
1402 }
1403 xprintf (this->xine, XINE_VERBOSITY_NONE,
1404 "metronom: unknown option in get_option: %d.\n", option);
1405 return 0;
1406 }
1407
metronom_set_master(metronom_t * this_gen,metronom_t * master)1408 static void metronom_set_master(metronom_t *this_gen, metronom_t *master) {
1409 metronom_impl_t *this = (metronom_impl_t *)this_gen;
1410 metronom_t *old_master = this->master;
1411
1412 pthread_mutex_lock(&this->lock);
1413 /* someone might currently be copying values from the old master,
1414 * so we need his lock too */
1415 if (old_master)
1416 old_master->set_option(old_master, METRONOM_LOCK, 1);
1417
1418 this->master = master;
1419 /* new master -> we have to reinit */
1420 this->disc.handled_count = 0;
1421
1422 if (old_master)
1423 old_master->set_option(old_master, METRONOM_LOCK, 0);
1424 pthread_mutex_unlock(&this->lock);
1425 }
1426
get_master_scr(metronom_clock_t * this)1427 static scr_plugin_t* get_master_scr(metronom_clock_t *this) {
1428 metronom_clock_private_t *this_priv = (metronom_clock_private_t *)this;
1429 scr_plugin_t *found = NULL, **r;
1430 int maxprio = 0;
1431
1432 /* find the SCR provider with the highest priority */
1433 for (r = this_priv->providers; *r && (r < this_priv->providers + MAX_SCR_PROVIDERS); r++) {
1434 int p = (*r)->get_priority (*r);
1435 if (maxprio < p) {
1436 found = *r;
1437 maxprio = p;
1438 }
1439 }
1440 if (!found) {
1441 xprintf (this_priv->mct.xine, XINE_VERBOSITY_NONE,
1442 "metronom: panic - no scr provider found!\n");
1443 return NULL;
1444 }
1445 return found;
1446 }
1447
metronom_sync_loop(void * const this_gen)1448 static void *metronom_sync_loop (void *const this_gen) {
1449 metronom_clock_private_t *const this_priv = (metronom_clock_private_t *const)this_gen;
1450
1451 struct timespec ts = {0, 0};
1452 scr_plugin_t **r;
1453 int64_t pts;
1454
1455 while (this_priv->mct.thread_running) {
1456 /* synchronise every 5 seconds */
1457 pthread_mutex_lock (&this_priv->mct.lock);
1458
1459 pts = this_priv->mct.scr_master->get_current (this_priv->mct.scr_master);
1460
1461 for (r = this_priv->providers; *r && (r < this_priv->providers + MAX_SCR_PROVIDERS); r++)
1462 if (*r != this_priv->mct.scr_master) (*r)->adjust (*r, pts);
1463
1464 xine_gettime (&ts);
1465 ts.tv_sec += 5;
1466 pthread_cond_timedwait (&this_priv->mct.cancel, &this_priv->mct.lock, &ts);
1467
1468 pthread_mutex_unlock (&this_priv->mct.lock);
1469 }
1470 return NULL;
1471 }
1472
metronom_start_sync_thread(metronom_clock_private_t * this_priv)1473 static void metronom_start_sync_thread (metronom_clock_private_t *this_priv) {
1474 int err;
1475
1476 if (this_priv->sync_thread_state == SYNC_THREAD_NONE) {
1477 this_priv->next_sync_pts = START_PTS;
1478 return;
1479 }
1480
1481 if (this_priv->sync_thread_state != SYNC_THREAD_OFF)
1482 return;
1483
1484 pthread_cond_init (&this_priv->mct.cancel, NULL);
1485
1486 this_priv->mct.thread_running = 1;
1487
1488 err = pthread_create (&this_priv->mct.sync_thread, NULL, metronom_sync_loop, &this_priv->mct);
1489
1490 if (err) {
1491 xprintf (this_priv->mct.xine, XINE_VERBOSITY_NONE,
1492 "metronom: cannot create sync thread (%s).\n", strerror (err));
1493 this_priv->next_sync_pts = START_PTS;
1494 } else {
1495 this_priv->sync_thread_state = SYNC_THREAD_RUNNING;
1496 this_priv->next_sync_pts = STOP_PTS;
1497 }
1498 }
1499
metronom_stop_sync_thread(metronom_clock_private_t * this_priv)1500 static void metronom_stop_sync_thread (metronom_clock_private_t *this_priv) {
1501
1502 if (this_priv->sync_thread_state == SYNC_THREAD_NONE) {
1503 this_priv->next_sync_pts = STOP_PTS;
1504 return;
1505 }
1506
1507 if (this_priv->sync_thread_state != SYNC_THREAD_RUNNING)
1508 return;
1509
1510 this_priv->mct.thread_running = 0;
1511
1512 pthread_mutex_lock (&this_priv->mct.lock);
1513 pthread_cond_signal (&this_priv->mct.cancel);
1514 pthread_mutex_unlock (&this_priv->mct.lock);
1515
1516 pthread_join (this_priv->mct.sync_thread, NULL);
1517
1518 pthread_cond_destroy (&this_priv->mct.cancel);
1519
1520 this_priv->sync_thread_state = SYNC_THREAD_OFF;
1521 this_priv->next_sync_pts = STOP_PTS;
1522 }
1523
metronom_register_scr(metronom_clock_t * this,scr_plugin_t * scr)1524 static int metronom_register_scr (metronom_clock_t *this, scr_plugin_t *scr) {
1525 metronom_clock_private_t *this_priv = (metronom_clock_private_t *)this;
1526 scr_plugin_t **r;
1527
1528 if (scr->interface_version != 3) {
1529 xprintf (this->xine, XINE_VERBOSITY_NONE,
1530 "metronom: wrong interface version for scr provider!\n");
1531 return -1;
1532 }
1533
1534 if (this_priv->providers[0] && !this_priv->providers[1])
1535 metronom_start_sync_thread (this_priv);
1536
1537 pthread_mutex_lock (&this_priv->mct.lock);
1538 for (r = this_priv->providers; *r && (r < this_priv->providers + MAX_SCR_PROVIDERS); r++) ;
1539 if (r >= this_priv->providers + MAX_SCR_PROVIDERS) {
1540 pthread_mutex_unlock (&this_priv->mct.lock);
1541 return -1; /* No free slot available */
1542 }
1543
1544 scr->clock = &this_priv->mct;
1545 *r = scr;
1546 this_priv->mct.scr_master = get_master_scr (&this_priv->mct);
1547 pthread_mutex_unlock (&this_priv->mct.lock);
1548 return 0;
1549 }
1550
metronom_unregister_scr(metronom_clock_t * this,scr_plugin_t * scr)1551 static void metronom_unregister_scr (metronom_clock_t *this, scr_plugin_t *scr) {
1552 metronom_clock_private_t *this_priv = (metronom_clock_private_t *)this;
1553 scr_plugin_t **found = NULL, **r;
1554 int64_t now;
1555
1556 if (!scr)
1557 return;
1558
1559 pthread_mutex_lock (&this_priv->mct.lock);
1560 /* never unregister scr_list[0]! */
1561 for (r = this_priv->providers + 1; *r && r < this_priv->providers + MAX_SCR_PROVIDERS; r++) {
1562 if (*r == scr)
1563 found = r;
1564 }
1565 if (!found) {
1566 pthread_mutex_unlock (&this_priv->mct.lock);
1567 return; /* Not found */
1568 }
1569 /* avoid holes in list */
1570 found[0] = r[-1];
1571 r[-1] = NULL;
1572
1573 now = this_priv->mct.scr_master->get_current (this_priv->mct.scr_master);
1574 /* master could have been adjusted, others must follow now */
1575 for (r = this_priv->providers; *r && (r < this_priv->providers + MAX_SCR_PROVIDERS); r++)
1576 if (*r != this_priv->mct.scr_master)
1577 (*r)->adjust (*r, now);
1578
1579 this_priv->mct.scr_master = get_master_scr (&this_priv->mct);
1580 pthread_mutex_unlock (&this_priv->mct.lock);
1581
1582 if (this_priv->providers[0] && !this_priv->providers[1])
1583 metronom_stop_sync_thread (this_priv);
1584 }
1585
metronom_exit(metronom_t * this_gen)1586 static void metronom_exit (metronom_t *this_gen) {
1587
1588 metronom_impl_t *this = (metronom_impl_t *)this_gen;
1589
1590 this->xine->config->unregister_callbacks (this->xine->config, NULL, NULL, this, sizeof (*this));
1591
1592 pthread_mutex_destroy (&this->lock);
1593 pthread_cond_destroy (&this->disc.video_reached);
1594 pthread_cond_destroy (&this->disc.audio_reached);
1595
1596 free (this);
1597 }
1598
metronom_clock_exit(metronom_clock_t * this)1599 static void metronom_clock_exit (metronom_clock_t *this) {
1600 metronom_clock_private_t *this_priv = (metronom_clock_private_t *)this;
1601 scr_plugin_t **r;
1602
1603 this_priv->mct.xine->config->unregister_callbacks (this_priv->mct.xine->config, NULL, NULL, this_priv, sizeof (*this_priv));
1604
1605 metronom_stop_sync_thread (this_priv);
1606
1607 pthread_mutex_lock (&this_priv->mct.lock);
1608 for (r = this_priv->providers; *r && (r < this_priv->providers + MAX_SCR_PROVIDERS); r++)
1609 (*r)->exit (*r);
1610 pthread_mutex_unlock (&this_priv->mct.lock);
1611
1612 pthread_mutex_destroy (&this_priv->mct.lock);
1613 free (this_priv);
1614 }
1615
1616
metronom_base_av_offs_hook(void * this_gen,xine_cfg_entry_t * entry)1617 static void metronom_base_av_offs_hook (void *this_gen, xine_cfg_entry_t *entry) {
1618 metronom_impl_t *this = (metronom_impl_t *)this_gen;
1619 this->video.base_av_offset = entry->num_value;
1620 }
1621
_x_metronom_init(int have_video,int have_audio,xine_t * xine)1622 metronom_t * _x_metronom_init (int have_video, int have_audio, xine_t *xine) {
1623
1624 metronom_impl_t *this = calloc(1, sizeof (metronom_impl_t));
1625 if (!this)
1626 return NULL;
1627 #ifndef HAVE_ZERO_SAFE_MEM
1628 /* Do these first, when compiler still knows "this" is all zeroed.
1629 * Let it optimize away this on most systems where clear mem
1630 * interpretes as 0, 0f or NULL safely.
1631 */
1632 this->master = NULL;
1633 this->vpts_offset = 0;
1634 this->audio.pts_per_smpls = 0;
1635 this->audio.last_pts = 0;
1636 this->audio.vpts_rmndr = 0;
1637 this->audio.vdr_hack = 0;
1638 this->audio.seek = 0;
1639 this->audio.samples = 0;
1640 this->audio.drift_step = 0;
1641 this->audio.force_jump = 0;
1642 this->video.last_pts = 0;
1643 this->video.av_offset = 0;
1644 this->video.drift = 0;
1645 this->video.drift_step = 0;
1646 this->video.img_cpt = 0;
1647 this->video.force_jump = 0;
1648 this->video.img_duration = 0;
1649 this->video.mode = 0;
1650 this->spu.vpts = 0;
1651 this->spu.offset = 0;
1652 this->bounce.diff = 0;
1653 this->bounce.vpts_offs = 0;
1654 this->bounce.jumped = 0;
1655 this->disc.video_count = 0;
1656 this->disc.handled_count = 0;
1657 this->disc.audio_count = 0;
1658 this->disc.num_audio_waiters = 0;
1659 this->disc.num_video_waiters = 0;
1660 this->disc.last_offs = 0;
1661 this->disc.last_type = 0;
1662 #endif
1663 this->bounce.left_audio = -1;
1664 this->bounce.left_video = -1;
1665 this->metronom.set_audio_rate = metronom_set_audio_rate;
1666 this->metronom.got_video_frame = metronom_got_video_frame;
1667 this->metronom.got_audio_samples = metronom_got_audio_samples;
1668 this->metronom.got_spu_packet = metronom_got_spu_packet;
1669 this->metronom.handle_audio_discontinuity = metronom_handle_audio_discontinuity;
1670 this->metronom.handle_video_discontinuity = metronom_handle_video_discontinuity;
1671 this->metronom.set_option = metronom_set_option;
1672 this->metronom.get_option = metronom_get_option;
1673 this->metronom.set_master = metronom_set_master;
1674 this->metronom.exit = metronom_exit;
1675
1676 this->xine = xine;
1677
1678 pthread_mutex_init (&this->lock, NULL);
1679
1680 this->prebuffer = PREBUFFER_PTS_OFFSET;
1681
1682 /* initialize video stuff */
1683
1684 this->disc.have_video = have_video;
1685 this->video.vpts = this->prebuffer;
1686 pthread_cond_init (&this->disc.video_reached, NULL);
1687 this->video.img_duration = 3000;
1688
1689 /* initialize audio stuff */
1690
1691 this->disc.have_audio = have_audio;
1692 this->audio.vpts = this->prebuffer;
1693 pthread_cond_init (&this->disc.audio_reached, NULL);
1694
1695 this->video.base_av_offset = xine->config->register_num (xine->config, "video.output.base_delay", 0,
1696 _("basic video to audio delay in pts"),
1697 _("Getting in sync picture and sound is a complex story.\n"
1698 "Xine will compensate for any delays it knows about.\n"
1699 "However, external hardware like flatscreens, sound systems, or simply\n"
1700 "the distance between you and the speakers may add in more.\n"
1701 "Here you can adjust video timing in steps of 1/90000 seconds manually."),
1702 10, metronom_base_av_offs_hook, this);
1703
1704 return &this->metronom;
1705 }
1706
1707
metronom_sync_hook(void * this_gen,xine_cfg_entry_t * entry)1708 static void metronom_sync_hook (void *this_gen, xine_cfg_entry_t *entry) {
1709 metronom_clock_private_t *this_priv = (metronom_clock_private_t *)this_gen;
1710
1711 if (entry->num_value) {
1712 if (this_priv->sync_thread_state != SYNC_THREAD_NONE)
1713 return;
1714 this_priv->sync_thread_state = SYNC_THREAD_OFF;
1715 this_priv->next_sync_pts = STOP_PTS;
1716 if (this_priv->providers[1])
1717 metronom_start_sync_thread (this_priv);
1718 } else {
1719 if (this_priv->sync_thread_state == SYNC_THREAD_NONE)
1720 return;
1721 metronom_stop_sync_thread (this_priv);
1722 this_priv->sync_thread_state = SYNC_THREAD_NONE;
1723 if (this_priv->providers[1])
1724 this_priv->next_sync_pts = START_PTS;
1725 }
1726 }
1727
_x_metronom_clock_init(xine_t * xine)1728 metronom_clock_t *_x_metronom_clock_init(xine_t *xine)
1729 {
1730 metronom_clock_private_t *this_priv = calloc (1, sizeof (metronom_clock_private_t));
1731
1732 if (!this_priv)
1733 return NULL;
1734 #ifndef HAVE_ZERO_SAFE_MEM
1735 this_priv->speed_change_used = 0;
1736 this_priv->speed_change_callbacks[0] = NULL;
1737 #endif
1738
1739 this_priv->mct.set_option = metronom_clock_set_option;
1740 this_priv->mct.get_option = metronom_clock_get_option;
1741 this_priv->mct.start_clock = metronom_start_clock;
1742 this_priv->mct.stop_clock = metronom_stop_clock;
1743 this_priv->mct.resume_clock = metronom_resume_clock;
1744 this_priv->mct.get_current_time = metronom_get_current_time;
1745 this_priv->mct.adjust_clock = metronom_adjust_clock;
1746 this_priv->mct.set_fine_speed = metronom_set_speed;
1747 this_priv->mct.register_scr = metronom_register_scr;
1748 this_priv->mct.unregister_scr = metronom_unregister_scr;
1749 this_priv->mct.exit = metronom_clock_exit;
1750
1751 this_priv->mct.register_speed_change_callback = metronom_register_speed_change_callback;
1752 this_priv->mct.unregister_speed_change_callback = metronom_unregister_speed_change_callback;
1753
1754 this_priv->mct.xine = xine;
1755 this_priv->mct.scr_adjustable = 1;
1756 this_priv->mct.scr_list = this_priv->providers;
1757
1758 pthread_mutex_init (&this_priv->mct.lock, NULL);
1759 this_priv->mct.register_scr (&this_priv->mct, unixscr_init (&this_priv->uscr));
1760
1761 this_priv->mct.thread_running = 0;
1762
1763 this_priv->next_sync_pts = STOP_PTS;
1764
1765 if (this_priv->mct.xine->config->register_bool (this_priv->mct.xine->config,
1766 "engine.use_metronom_sync_thread", 0,
1767 _("Sync multiple clocks in a separate thread"),
1768 _("Enable this when there are problems with multiple (eg application supplied) clocks."),
1769 20, metronom_sync_hook, this_priv)) {
1770 this_priv->sync_thread_state = SYNC_THREAD_OFF;
1771 } else {
1772 this_priv->sync_thread_state = SYNC_THREAD_NONE;
1773 }
1774
1775 return &this_priv->mct;
1776 }
1777
1778