[vlc-devel] [RFC PATCH 09/13] FIXUP: implement audio/video/spu delay

Thomas Guillem thomas at gllm.fr
Wed Jun 27 14:41:31 CEST 2018


Each ESses will set the delay to their specific clocks.
Setting a delay on the master clock has an effect on all others slaves clocks.
Setting a delay on a slave clock has only an effect for the current clock.

For the audio master case (most common case), the aout need to wait (play
silence) when adding a positive delay to the master clock.
---
 src/audio_output/aout_internal.h    |  3 ++
 src/audio_output/dec.c              | 24 +++++++++++++
 src/clock/clock.c                   | 42 ++++++++++++++++++++++
 src/clock/clock.h                   |  7 ++++
 src/input/decoder.c                 | 56 +++++++++++++++++++++++------
 src/video_output/control.h          | 13 ++++++-
 src/video_output/video_output.c     | 53 ++++++++++++++++++++++++---
 src/video_output/vout_internal.h    | 15 ++++++++
 src/video_output/vout_subpictures.c |  6 ++++
 9 files changed, 203 insertions(+), 16 deletions(-)

diff --git a/src/audio_output/aout_internal.h b/src/audio_output/aout_internal.h
index 9437b13cfb..b1f658b9eb 100644
--- a/src/audio_output/aout_internal.h
+++ b/src/audio_output/aout_internal.h
@@ -78,6 +78,8 @@ typedef struct
         vlc_tick_t resamp_start_drift; /**< Resampler drift absolute value */
         int resamp_type; /**< Resampler mode (FIXME: redundant / resampling) */
         bool discontinuity;
+        vlc_tick_t request_delay;
+        vlc_tick_t delay;
     } sync;
 
     int requested_stereo_mode; /**< Requested stereo mode set by the user */
@@ -149,6 +151,7 @@ int aout_DecPlay(audio_output_t *aout, block_t *block);
 void aout_DecGetResetStats(audio_output_t *, unsigned *, unsigned *);
 void aout_DecChangePause(audio_output_t *, bool b_paused, vlc_tick_t i_date);
 void aout_DecChangeRate(audio_output_t *aout, float rate);
+void aout_DecChangeDelay(audio_output_t *aout, vlc_tick_t delay);
 void aout_DecFlush(audio_output_t *, bool wait);
 void aout_RequestRestart (audio_output_t *, unsigned);
 void aout_RequestRetiming(audio_output_t *aout, vlc_tick_t audio_ts,
diff --git a/src/audio_output/dec.c b/src/audio_output/dec.c
index 3f3a6da702..59a1c065c3 100644
--- a/src/audio_output/dec.c
+++ b/src/audio_output/dec.c
@@ -106,6 +106,7 @@ error:
     owner->sync.end = VLC_TS_INVALID;
     owner->sync.resamp_type = AOUT_RESAMPLING_NONE;
     owner->sync.discontinuity = true;
+    owner->sync.delay = owner->sync.request_delay = 0;
 
     atomic_init (&owner->buffers_lost, 0);
     atomic_init (&owner->buffers_played, 0);
@@ -230,6 +231,16 @@ static void aout_DecSilence (audio_output_t *aout, vlc_tick_t length, vlc_tick_t
 
 static void aout_DecSynchronize(audio_output_t *aout, vlc_tick_t dec_pts)
 {
+    aout_owner_t *owner = aout_owner (aout);
+
+    if (owner->sync.request_delay != owner->sync.delay)
+    {
+        owner->sync.delay = owner->sync.request_delay;
+        vlc_tick_t delta = vlc_clock_SetDelay(owner->sync.clock, owner->sync.delay);
+        if (delta > 0)
+            aout_DecSilence (aout, delta, dec_pts);
+    }
+
     vlc_tick_t now = vlc_tick_now();
     vlc_tick_t delay;
 
@@ -390,6 +401,12 @@ int aout_DecPlay(audio_output_t *aout, block_t *block)
     /* Drift correction */
     aout_DecSynchronize(aout, block->i_pts);
 
+    if (owner->sync.delay != 0)
+    {
+        block->i_pts += owner->sync.delay;
+        block->i_dts += owner->sync.delay;
+    }
+
     /* Output */
     owner->sync.end = block->i_pts + block->i_length + 1;
     owner->sync.discontinuity = false;
@@ -446,6 +463,13 @@ void aout_DecChangeRate(audio_output_t *aout, float rate)
     owner->sync.rate = rate;
 }
 
+void aout_DecChangeDelay(audio_output_t *aout, vlc_tick_t delay)
+{
+    aout_owner_t *owner = aout_owner(aout);
+
+    owner->sync.request_delay = delay;
+}
+
 void aout_DecFlush (audio_output_t *aout, bool wait)
 {
     aout_owner_t *owner = aout_owner (aout);
diff --git a/src/clock/clock.c b/src/clock/clock.c
index ecf5e9db56..cbba4bca09 100644
--- a/src/clock/clock.c
+++ b/src/clock/clock.c
@@ -62,6 +62,7 @@ struct vlc_clock_t
                       vlc_tick_t system_now, float rate);
     void (*reset)(vlc_clock_t * clock);
     void (*pause)(vlc_clock_t * clock, bool paused, vlc_tick_t now);
+    vlc_tick_t (*set_delay)(vlc_clock_t * clock, vlc_tick_t delay);
     void (*set_dejitter)(vlc_clock_t * clock, vlc_tick_t delay, int cr_avg);
     vlc_tick_t (*to_system_locked)(vlc_clock_t * clock, vlc_tick_t now, vlc_tick_t pts);
 
@@ -183,6 +184,28 @@ static void vlc_clock_master_pause(vlc_clock_t * clock, bool paused, vlc_tick_t
     vlc_mutex_unlock(&main_clock->lock);
 }
 
+static vlc_tick_t vlc_clock_master_set_delay(vlc_clock_t * clock, vlc_tick_t delay)
+{
+    vlc_clock_main_t * main_clock = clock->owner;
+    vlc_mutex_lock(&main_clock->lock);
+
+    vlc_tick_t delta = delay - main_clock->delay;
+    main_clock->delay = delay;
+    if (delta > 0)
+    {
+        if (main_clock->reset_date == VLC_TS_INVALID)
+            main_clock->reset_date = vlc_tick_now() + delta;
+        else
+            main_clock->reset_date += delta;
+    }
+    else
+        delta = 0;
+
+    vlc_cond_broadcast(&main_clock->cond);
+    vlc_mutex_unlock(&main_clock->lock);
+    return delta;
+}
+
 static float vlc_clock_get_rate(vlc_clock_t * clock)
 {
     float rate;
@@ -295,6 +318,18 @@ static void vlc_clock_slave_pause(vlc_clock_t * clock, bool paused, vlc_tick_t n
     VLC_UNUSED(now);
 }
 
+static vlc_tick_t vlc_clock_slave_set_delay(vlc_clock_t * clock, vlc_tick_t delay)
+{
+    vlc_clock_main_t * main_clock = clock->owner;
+    vlc_mutex_lock(&main_clock->lock);
+
+    clock->delay = delay;
+
+    vlc_cond_broadcast(&main_clock->cond);
+    vlc_mutex_unlock(&main_clock->lock);
+    return 0;
+}
+
 int vlc_clock_Wait(vlc_clock_t * clock, vlc_tick_t pts, vlc_tick_t max_duration)
 {
     vlc_clock_main_t * main_clock = clock->owner;
@@ -404,6 +439,11 @@ void vlc_clock_ChangePause(vlc_clock_t * clock, bool paused, vlc_tick_t system_n
     clock->pause(clock, paused, system_now);
 }
 
+vlc_tick_t vlc_clock_SetDelay(vlc_clock_t * clock, vlc_tick_t delay)
+{
+    return clock->set_delay(clock, delay);
+}
+
 float vlc_clock_GetRate(vlc_clock_t * clock)
 {
     return vlc_clock_get_rate(clock);
@@ -449,6 +489,7 @@ static void vlc_clock_set_master_cbk(vlc_clock_t * clk)
     clk->update = vlc_clock_master_update;
     clk->reset = vlc_clock_master_reset;
     clk->pause = vlc_clock_master_pause;
+    clk->set_delay = vlc_clock_master_set_delay;
     clk->set_dejitter = vlc_clock_master_set_dejitter;
     clk->to_system_locked = vlc_clock_master_to_system_locked;
 }
@@ -458,6 +499,7 @@ static void vlc_clock_set_slave_cbk(vlc_clock_t * clk)
     clk->update = vlc_clock_slave_update;
     clk->reset = vlc_clock_slave_reset;
     clk->pause = vlc_clock_slave_pause;
+    clk->set_delay = vlc_clock_slave_set_delay;
     clk->set_dejitter = vlc_clock_slave_set_dejitter;
     clk->to_system_locked = vlc_clock_slave_to_system_locked;
 }
diff --git a/src/clock/clock.h b/src/clock/clock.h
index 51e34f34e1..41469a9274 100644
--- a/src/clock/clock.h
+++ b/src/clock/clock.h
@@ -91,6 +91,13 @@ void vlc_clock_Reset(vlc_clock_t * clock);
 void vlc_clock_ChangePause(vlc_clock_t * clock, bool paused,
                            vlc_tick_t sysem_now);
 
+/**
+ * This functions change the clock delay.
+ * It returns the amount of time the clock owner need to wait in order to reach
+ * the time introduced by the new positive delay.
+ */
+vlc_tick_t vlc_clock_SetDelay(vlc_clock_t * clock, vlc_tick_t pts_delay);
+
 /**
  * This function returns the current rate.
  */
diff --git a/src/input/decoder.c b/src/input/decoder.c
index 4650850e17..2597431997 100644
--- a/src/input/decoder.c
+++ b/src/input/decoder.c
@@ -111,6 +111,7 @@ struct decoder_owner
     vlc_tick_t i_preroll_end;
     /* Pause & Rate */
     vlc_tick_t pause_date;
+    vlc_tick_t delay;
     float rate;
     unsigned frames_countdown;
     bool paused;
@@ -136,9 +137,6 @@ struct decoder_owner
         decoder_cc_desc_t desc;
         decoder_t *pp_decoder[MAX_CC_DECODERS];
     } cc;
-
-    /* Delay */
-    vlc_tick_t i_ts_delay;
 };
 
 /* Pictures which are DECODER_BOGUS_VIDEO_DELAY or more in advance probably have
@@ -1433,6 +1431,31 @@ static void OutputChangeRate( decoder_t *p_dec, float rate )
     }
 }
 
+static void OutputChangeDelay( decoder_t *p_dec, vlc_tick_t delay )
+{
+    struct decoder_owner *p_owner = dec_get_owner( p_dec );
+
+    msg_Dbg( p_dec, "changing delay: %"PRId64, delay );
+    switch( p_dec->fmt_out.i_cat )
+    {
+        case VIDEO_ES:
+            if( p_owner->p_vout != NULL )
+                vout_ChangeDelay( p_owner->p_vout, delay );
+            break;
+        case AUDIO_ES:
+            if( p_owner->p_aout != NULL )
+                aout_DecChangeDelay( p_owner->p_aout, delay );
+            break;
+        case SPU_ES:
+            if( p_owner->p_vout != NULL )
+                vout_ChangeSpuDelay( p_owner->p_vout, p_owner->i_spu_channel,
+                                     delay );
+            break;
+        default:
+            vlc_assert_unreachable();
+    }
+}
+
 /**
  * The decoding main loop
  *
@@ -1443,6 +1466,7 @@ static void *DecoderThread( void *p_data )
     decoder_t *p_dec = (decoder_t *)p_data;
     struct decoder_owner *p_owner = dec_get_owner( p_dec );
     float rate = 1.f;
+    vlc_tick_t delay = 0;
     bool paused = false;
 
     /* The decoder's main loop */
@@ -1500,6 +1524,19 @@ static void *DecoderThread( void *p_data )
             vlc_fifo_Lock( p_owner->p_fifo );
         }
 
+        if( delay != p_owner->delay )
+        {
+            int canc = vlc_savecancel();
+
+            delay = p_owner->delay;
+            vlc_fifo_Unlock( p_owner->p_fifo );
+
+            OutputChangeDelay( p_dec, delay );
+
+            vlc_restorecancel( canc );
+            vlc_fifo_Lock( p_owner->p_fifo );
+        }
+
         if( p_owner->paused && p_owner->frames_countdown == 0 )
         {   /* Wait for resumption from pause */
             p_owner->b_idle = true;
@@ -1620,6 +1657,7 @@ static decoder_t * CreateDecoder( vlc_object_t *p_parent,
     p_owner->b_fmt_description = false;
     p_owner->p_description = NULL;
 
+    p_owner->delay = 0;
     p_owner->rate = 1.f;
     p_owner->paused = false;
     p_owner->pause_date = VLC_TS_INVALID;
@@ -1722,7 +1760,6 @@ static decoder_t * CreateDecoder( vlc_object_t *p_parent,
     p_owner->cc.desc.i_708_channels = 0;
     for( unsigned i = 0; i < MAX_CC_DECODERS; i++ )
         p_owner->cc.pp_decoder[i] = NULL;
-    p_owner->i_ts_delay = 0;
     return p_dec;
 }
 
@@ -2215,17 +2252,16 @@ void input_DecoderChangeRate( decoder_t *dec, float rate )
 
     vlc_fifo_Lock( owner->p_fifo );
     owner->rate = rate;
-    vlc_fifo_Signal( owner->p_fifo );
     vlc_fifo_Unlock( owner->p_fifo );
 }
 
-void input_DecoderChangeDelay( decoder_t *p_dec, vlc_tick_t i_delay )
+void input_DecoderChangeDelay( decoder_t *dec, vlc_tick_t delay )
 {
-    struct decoder_owner *p_owner = dec_get_owner( p_dec );
+    struct decoder_owner *owner = dec_get_owner( dec );
 
-    vlc_mutex_lock( &p_owner->lock );
-    p_owner->i_ts_delay = i_delay;
-    vlc_mutex_unlock( &p_owner->lock );
+    vlc_fifo_Lock( owner->p_fifo );
+    owner->delay = delay;
+    vlc_fifo_Unlock( owner->p_fifo );
 }
 
 void input_DecoderStartWait( decoder_t *p_dec )
diff --git a/src/video_output/control.h b/src/video_output/control.h
index ad04dff963..70117c7507 100644
--- a/src/video_output/control.h
+++ b/src/video_output/control.h
@@ -48,7 +48,9 @@ enum {
     VOUT_CONTROL_CHANGE_SUB_MARGIN,     /* integer */
 
     VOUT_CONTROL_PAUSE,
-    VOUT_CONTROL_CHANGE_RATE,           /* float */
+    VOUT_CONTROL_CHANGE_RATE,           /* rate */
+    VOUT_CONTROL_CHANGE_DELAY,          /* delay */
+    VOUT_CONTROL_CHANGE_SPU_DELAY,      /* spu_delay */
     VOUT_CONTROL_FLUSH,                 /* time */
     VOUT_CONTROL_STEP,                  /* time_ptr */
 
@@ -85,6 +87,7 @@ typedef struct {
             vlc_tick_t date;
         } pause;
         float rate;
+        vlc_tick_t delay;
         struct {
             int channel;
             char *string;
@@ -101,6 +104,14 @@ typedef struct {
             unsigned width;
             unsigned height;
         } window;
+        struct {
+            int channel;
+            float value;
+        } spu_rate;
+        struct {
+            int channel;
+            vlc_tick_t value;
+        } spu_delay;
         vlc_mouse_t mouse;
         const vout_configuration_t *cfg;
         subpicture_t *subpicture;
diff --git a/src/video_output/video_output.c b/src/video_output/video_output.c
index 67858b1a91..adc3240eb2 100644
--- a/src/video_output/video_output.c
+++ b/src/video_output/video_output.c
@@ -133,7 +133,7 @@ static vout_thread_t *VoutCreate(vlc_object_t *object,
     /* */
     vout->p = (vout_thread_sys_t*)&vout[1];
 
-    vout->p->rate = 1;
+    vout->p->rate = 1.f;
     vout->p->clock = cfg->clock;
     vout->p->original = original;
     vout->p->dpb_size = cfg->dpb_size;
@@ -338,6 +338,27 @@ void vout_ChangeRate(vout_thread_t *vout, float rate)
     vout_control_WaitEmpty(&vout->p->control);
 }
 
+void vout_ChangeDelay(vout_thread_t *vout, vlc_tick_t delay)
+{
+    vout_control_cmd_t cmd;
+    vout_control_cmd_Init(&cmd, VOUT_CONTROL_CHANGE_DELAY);
+    cmd.delay = delay;
+    vout_control_Push(&vout->p->control, &cmd);
+
+    vout_control_WaitEmpty(&vout->p->control);
+}
+
+void vout_ChangeSpuDelay(vout_thread_t *vout, int channel, vlc_tick_t delay)
+{
+    vout_control_cmd_t cmd;
+    vout_control_cmd_Init(&cmd, VOUT_CONTROL_CHANGE_SPU_DELAY);
+    cmd.spu_delay.channel = channel;
+    cmd.spu_delay.value = delay;
+    vout_control_Push(&vout->p->control, &cmd);
+
+    vout_control_WaitEmpty(&vout->p->control);
+}
+
 void vout_GetResetStatistic(vout_thread_t *vout, unsigned *restrict displayed,
                             unsigned *restrict lost)
 {
@@ -1000,8 +1021,8 @@ static int ThreadDisplayRenderPicture(vout_thread_t *vout, bool is_forced)
     if (vout->p->pause.is_on)
         render_subtitle_date = vout->p->pause.date;
     else
-        render_subtitle_date = filtered->date > 1 ? filtered->date : vlc_tick_now();
-    vlc_tick_t render_osd_date = vlc_tick_now(); /* FIXME wrong */
+        render_subtitle_date =
+            vlc_clock_ConvertToSystem(vout->p->clock, system_now, filtered->date);
 
     /*
      * Get the subpicture to be displayed
@@ -1330,9 +1351,18 @@ static void ThreadChangePause(vout_thread_t *vout, bool is_paused, vlc_tick_t da
         vlc_clock_ChangePause(vout->p->clock, is_paused, date);
 }
 
-static void ThreadChangeRate(vout_thread_t *vout, float rate)
+static void ThreadChangeDelay(vout_thread_t *vout, vlc_tick_t delay)
 {
-    vout->p->rate = rate;
+    if (vout->p->clock)
+        vlc_clock_SetDelay(vout->p->clock, delay);
+}
+
+static void ThreadChangeSpuDelay(vout_thread_t *vout, int channel, vlc_tick_t delay)
+{
+    vlc_mutex_lock(&vout->p->spu_lock);
+    if (vout->p->spu)
+        spu_SetClockDelay(vout->p->spu, delay);
+    vlc_mutex_unlock(&vout->p->spu_lock);
 }
 
 static void ThreadFlush(vout_thread_t *vout, bool below, vlc_tick_t date)
@@ -1360,6 +1390,13 @@ static void ThreadFlush(vout_thread_t *vout, bool below, vlc_tick_t date)
         vlc_clock_Reset(vout->p->clock);
 }
 
+static void ThreadChangeRate(vout_thread_t *vout, float rate)
+{
+    if (rate != vout->p->rate)
+        ThreadFlush(vout, false, 0);
+    vout->p->rate = rate;
+}
+
 static void ThreadStep(vout_thread_t *vout, vlc_tick_t *duration)
 {
     *duration = 0;
@@ -1754,6 +1791,12 @@ static int ThreadControl(vout_thread_t *vout, vout_control_cmd_t cmd)
     case VOUT_CONTROL_CHANGE_RATE:
         ThreadChangeRate(vout, cmd.rate);
         break;
+    case VOUT_CONTROL_CHANGE_DELAY:
+        ThreadChangeDelay(vout, cmd.delay);
+        break;
+    case VOUT_CONTROL_CHANGE_SPU_DELAY:
+        ThreadChangeSpuDelay(vout, cmd.spu_delay.channel, cmd.spu_delay.value);
+        break;
     case VOUT_CONTROL_FLUSH:
         ThreadFlush(vout, false, cmd.time);
         break;
diff --git a/src/video_output/vout_internal.h b/src/video_output/vout_internal.h
index 2f0d52c44e..4e57dad406 100644
--- a/src/video_output/vout_internal.h
+++ b/src/video_output/vout_internal.h
@@ -63,6 +63,7 @@ struct vout_thread_sys_t
 
     vlc_clock_t     *clock;
     float           rate;
+    vlc_tick_t      delay;
 
     /* Input thread for dvd menu interactions */
     vlc_object_t    *input;
@@ -225,6 +226,7 @@ int spu_ProcessMouse(spu_t *, const vlc_mouse_t *, const video_format_t *);
 void spu_Attach( spu_t *, vlc_object_t *input, bool );
 void vout_SetSubpictureClock( vout_thread_t *vout, vlc_clock_t *clock ); /* FIXME */
 void spu_SetClock( spu_t *, vlc_clock_t * );
+void spu_SetClockDelay(spu_t *spu, vlc_tick_t delay);
 void spu_ChangeMargin(spu_t *, int);
 
 /**
@@ -239,6 +241,19 @@ void vout_ChangePause( vout_thread_t *, bool b_paused, vlc_tick_t i_date );
  */
 void vout_ChangeRate( vout_thread_t *, float rate );
 
+/**
+ * This function will change the delay of the vout
+ * It is thread safe
+ */
+void vout_ChangeDelay( vout_thread_t *, vlc_tick_t delay );
+
+/**
+ * This function will change the delay of the spu channel
+ * It is thread safe
+ */
+void vout_ChangeSpuDelay( vout_thread_t *, int spu_channel, vlc_tick_t delay );
+
+
 /**
  * Updates the pointing device state.
  */
diff --git a/src/video_output/vout_subpictures.c b/src/video_output/vout_subpictures.c
index dc3e83ede3..066f615603 100644
--- a/src/video_output/vout_subpictures.c
+++ b/src/video_output/vout_subpictures.c
@@ -1485,6 +1485,12 @@ void spu_SetClock(spu_t *spu, vlc_clock_t *clock)
     spu->p->clock = clock;
 }
 
+void spu_SetClockDelay(spu_t *spu, vlc_tick_t delay)
+{
+    if (spu->p->clock)
+        vlc_clock_SetDelay(spu->p->clock, delay);
+}
+
 /**
  * Inform the SPU filters of mouse event
  */
-- 
2.18.0



More information about the vlc-devel mailing list