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