[vlc-devel] [PATCH 4/5] Subtitle Sync : added a convenient way to adjust subtitle fps

Pascal Thomet pthomet at gmail.com
Mon Jun 9 21:00:26 CEST 2014


Principle:
**********
* All of this is limited to *file* subtitles (DVD and DVB are not concerned)
* If the user needs to sync twice the delay, and if he does this at two different positions
  in the movie (separated by at least 45 seconds), a fps ratio will be calculated and proposed
  if it seems to be valid.
* The user will be asked to re-confirm the speed adjustment
* Care has been taken in order to avoid user errors:
  The subtitle fps will only be adjusted if the calculated speed ratio is close to a valid one.
  The only fps considered are { 23.976f, 24.f, 25.f, 30.f } (most common movies fps)
  A valid ratio is under the form a / b
  where a and b are members of { 23.976f, 24.f, 25.f, 30.f }
---
 .../macosx/Resources/English.lproj/SyncTracks.xib  |  22 +-
 modules/control/hotkeys.c                          |  18 +-
 modules/demux/subtitle.c                           | 358 +++++++++++++++++++--
 modules/gui/qt4/components/extended_panels.cpp     |  18 +-
 src/input/var.c                                    |   1 -
 5 files changed, 383 insertions(+), 34 deletions(-)

diff --git a/extras/package/macosx/Resources/English.lproj/SyncTracks.xib b/extras/package/macosx/Resources/English.lproj/SyncTracks.xib
index bf0ecaa..9cb68a6 100644
--- a/extras/package/macosx/Resources/English.lproj/SyncTracks.xib
+++ b/extras/package/macosx/Resources/English.lproj/SyncTracks.xib
@@ -31,10 +31,24 @@
         subsVisualSyncLayout->addWidget( buttonBookmarkAudio, 0, 0, 1, 1 );
         CONNECT( buttonBookmarkAudio, pressed(), this, bookmarkAudio() ) ;
 
-        QPushButton *buttonBookmarkSubtitle = new QPushButton(_("Bookmark subtitle"));
-        buttonBookmarkSubtitle->setToolTip(_("You can also use Shift-J on the video"));
-        subsVisualSyncLayout->addWidget( buttonBookmarkSubtitle, 0, 1, 1, 1 );
-        CONNECT( buttonBookmarkSubtitle, pressed(), this, bookmarkSubtitle() ) ;
+        QPushButton *buttonBookmarkSync = new QPushButton(_("Sync subtitles"));
+        QString buttonBookmarkSync_Tooltip = qtr( 
+            "You can also use Shift-K on the video\n"\
+            "\n"\
+            "\n"\
+            "If the subtitle has a different fps,\n"\
+            "(ie the delay varies during the movie):\n"\
+            "\n"\
+            "* sync the subtitles at the beginning of the video\n"\
+            "* fast forward a few minutes in the movie\n"\
+            "* then resync the subtitles.\n"\
+            "\n"\
+            "If applicable, you will be asked to change the fps\n"\
+            "of the subtitle.\n"\
+        );
+        buttonBookmarkSync->setToolTip( buttonBookmarkSync_Tooltip );
+        subsVisualSyncLayout->addWidget( buttonBookmarkSync, 0, 2, 1, 1 );
+        CONNECT( buttonBookmarkSync, pressed(), this, syncBookmarks() ) ;
 
         QPushButton *buttonBookmarkSync = new QPushButton(_("Sync subtitles"));
         buttonBookmarkSync->setToolTip(_("You can also use Shift-K on the video"));
diff --git a/modules/control/hotkeys.c b/modules/control/hotkeys.c
index e32c389..a45346d 100644
--- a/modules/control/hotkeys.c
+++ b/modules/control/hotkeys.c
@@ -1013,6 +1013,14 @@ static int ActionEvent( vlc_object_t *libvlc, char const *psz_var,
 
     if ( strcmp( psz_var, "key-osdmessage") == 0)
     {
+        /* Add stars around the OSD to increase its duration (this is a hack, yes! )*/
+        int osd_duration = 1000000;
+        if ( ( strlen(newval.psz_string) > 0) && ( newval.psz_string[0] == '*' ) )
+                osd_duration *= 2;
+        if ( ( strlen(newval.psz_string) > 1) && ( newval.psz_string[1] == '*' ) )
+                osd_duration *= 2;
+        if ( ( strlen(newval.psz_string) > 2) && ( newval.psz_string[1] == '*' ) )
+                osd_duration *= 2;
         if ( strlen(newval.psz_string) > 0 )
         {
             playlist_t *p_playlist = pl_Get( p_intf );
@@ -1022,12 +1030,18 @@ static int ActionEvent( vlc_object_t *libvlc, char const *psz_var,
                 vout_thread_t *p_vout = p_input ? input_GetVout( p_input ) : NULL;// XXXXDZFEEGVDS
                 if( p_vout )
                 {
-                    DisplayMessage(p_vout, "%s", newval.psz_string);
+                    do 
+                    {
+                        vout_OSDText(
+                                p_vout, SPU_DEFAULT_CHANNEL,
+                                SUBPICTURE_ALIGN_TOP|SUBPICTURE_ALIGN_LEFT, 
+                                osd_duration,
+                                newval.psz_string);
+                    } while(0);
                     vlc_object_release( p_vout );
                 }
                 vlc_object_release(p_input);
             }
-
         }
         return VLC_SUCCESS;        
     }
diff --git a/modules/demux/subtitle.c b/modules/demux/subtitle.c
index 81d0a76..4d72061 100644
--- a/modules/demux/subtitle.c
+++ b/modules/demux/subtitle.c
@@ -30,7 +30,7 @@
 #ifdef HAVE_CONFIG_H
 # include "config.h"
 #endif
-
+#include <math.h>
 #include <vlc_common.h>
 #include <vlc_plugin.h>
 #include <vlc_input.h>
@@ -144,6 +144,23 @@ typedef struct
     char    *psz_text;
 } subtitle_t;
 
+/* The only fps considered valid are : { 23.976f, 24.f, 25.f, 30.f }
+ * 13 = 1 + nb of partial permutations of 2 elements in a set  of 4 := 1 + 4! / (4 - 2 ) ! 
+ * We add one in order to store a fps_ratio = 1.f
+ */
+#define NB_SUB_FPS_ADJUSTS 13
+typedef struct sub_fps_adjust
+{
+    float fps_ratio;
+    char  sz_desc[15];
+} sub_fps_adjust;
+
+struct sub_speed_delay
+{
+    int64_t sub_delay;
+    int idx_sub_fps_adjust;
+};
+
 
 struct demux_sys_t
 {
@@ -184,8 +201,17 @@ struct demux_sys_t
     {
         int64_t i_time_subtitle;
         int64_t i_time_audio;
+        int64_t i_time_subtitle_n_1;
+        int64_t i_time_audio_n_1;
     } subtitle_delaybookmarks;
 
+    /*lists of possible subtitle fps adjustments*/
+    sub_fps_adjust sub_fps_adjusts[NB_SUB_FPS_ADJUSTS];
+    
+    /* holds a delay + speed change that needs user confirmation*/
+    struct sub_speed_delay sub_speed_delay_user_confirm;
+    /*holds the current speed and delay*/
+    struct sub_speed_delay sub_speed_delay_current;
 };
 
 static int  ParseMicroDvd   ( demux_t *, subtitle_t *, int );
@@ -249,6 +275,290 @@ static int set_current_subtitle_by_time(demux_t *p_demux, int64_t i64_when);
 
 
 /*****************************************************************************
+ * Compute subtitles delay and speed
+ *****************************************************************************/
+
+static float GetDemuxSubSpeed(demux_t *p_demux)
+{
+    return p_demux->p_sys->sub_fps_adjusts[ p_demux->p_sys->sub_speed_delay_current.idx_sub_fps_adjust ].fps_ratio;
+}
+
+static int64_t GetDemuxSubDelay(demux_t *p_demux)
+{
+    return p_demux->p_sys->sub_speed_delay_current.sub_delay;
+}
+
+static void SetDemuxSubDelay(demux_t *p_demux, int64_t delay)
+{
+    p_demux->p_sys->sub_speed_delay_current.sub_delay = delay;
+    var_SetTime(p_demux->p_parent, "spu-delay", delay);
+}
+
+static void LogDelays(demux_t *p_demux)
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+    msg_Dbg(p_demux,
+            "ApplySubtitleDelayAndSpeed : audio0=%li sub0=%li audio1=%li sub1=%li",
+            (p_sys->subtitle_delaybookmarks.i_time_audio_n_1) / 1000,
+            (p_sys->subtitle_delaybookmarks.i_time_subtitle_n_1) / 1000,
+            (p_sys->subtitle_delaybookmarks.i_time_audio) / 1000,
+            (p_sys->subtitle_delaybookmarks.i_time_subtitle) / 1000
+            );
+}
+static void Log_speed_delay(demux_t *p_demux)
+{
+    int64_t sub_delay = GetDemuxSubDelay(p_demux);
+    float sub_speed = GetDemuxSubSpeed(p_demux);
+    msg_Dbg(p_demux,
+            "sub_speed: %.2f spu-delay : %li",
+            sub_speed,
+            sub_delay / 1000
+            );       
+}
+
+/* Fills the lists of possible subtitle fps adjustments
+ * The only fps considered valid are : 
+ * { 23.976f, 24.f, 25.f, 30.f }
+ * Subtitle sync will only propose speed adjustments 
+ * with valid ratios.
+ * A valid ratio is under the form a / b
+ * where a and b are members of { 23.976f, 24.f, 25.f, 30.f }
+ */
+static void fill_sub_fps_adjusts(demux_t *p_demux)
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+
+    float fps_list[] = { 23.976f, 24.f, 25.f, 30.f };
+    int i, j;
+    float nb_fps = 4;
+
+    p_sys->sub_fps_adjusts[0].fps_ratio = 1.f;
+    p_sys->sub_fps_adjusts[0].sz_desc[0] = '\0';
+
+    int cur_id = 1;//will vary between 1 and NB_SUB_FPS_ADJUSTS - 1
+    for (i = 0; i < nb_fps; i++)
+    {
+        for (j = 0; j < nb_fps; j++)
+        {
+            if (j != i)
+            {
+                p_sys->sub_fps_adjusts[ cur_id ].fps_ratio = fps_list[i] / fps_list[j];
+                snprintf(p_sys->sub_fps_adjusts[ cur_id ].sz_desc, 15, 
+                        "%.6g->%.6g fps", fps_list[i], fps_list[j]);
+                cur_id ++;
+            }
+        }
+    }
+}
+
+/*
+ * Returns 1 if a change of subtitle speed (aka fps) is advisable, 0 otherwise
+ */
+static int ComputeSubSpeedAndDelay( demux_t *p_demux, struct sub_speed_delay * out_sub_speed_delay )
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+
+    /* If we do not have older bookmarks, cancel! */
+    if (       ( p_sys->subtitle_delaybookmarks.i_time_audio_n_1 <= 0 ) 
+            || ( p_sys->subtitle_delaybookmarks.i_time_subtitle_n_1 <= 0 ) )
+    {
+        return 0;
+    }
+
+    /*No need to do it if there is less than 45 seconds between now and the last bookmarks 
+    (the user is probably readjusting the delay)*/
+    int64_t min_delay = 45 * 1000 * 1000;
+    if (    p_sys->subtitle_delaybookmarks.i_time_subtitle 
+          - p_sys->subtitle_delaybookmarks.i_time_subtitle_n_1 < min_delay )
+    {
+        return 0;
+    }
+
+    /* If the speed was already adjusted, cancel, 
+     * unless we have a duration > 5 minutes between the two samples, in which
+     * case, we can fine-tune the speed
+     */
+    int64_t min_delay_with_speed_already_set = 60 * 5 * 1000 * 1000;
+    if  (    ( p_sys->sub_speed_delay_current.idx_sub_fps_adjust != 0 )
+          && (   p_sys->subtitle_delaybookmarks.i_time_subtitle 
+               - p_sys->subtitle_delaybookmarks.i_time_subtitle_n_1 < min_delay_with_speed_already_set )
+        )
+    {
+        return 0;
+    }
+
+    float audio0 = (float)p_sys->subtitle_delaybookmarks.i_time_audio_n_1 / 1000.f;
+    float subtitle0 = p_sys->subtitle_delaybookmarks.i_time_subtitle_n_1 / 1000.f;
+    float audio1 = (float)p_sys->subtitle_delaybookmarks.i_time_audio / 1000.f;
+    float subtitle1 = p_sys->subtitle_delaybookmarks.i_time_subtitle / 1000.f;
+
+    float time0 = __MAX(audio0, subtitle0);
+    float time1 = __MAX(audio1, subtitle1);
+    int64_t i_delay0 = GetDemuxSubDelay(p_demux);
+    float delay0 = (float)i_delay0 / 1000.f;
+    int64_t i_additional_subdelay = p_sys->subtitle_delaybookmarks.i_time_audio - p_sys->subtitle_delaybookmarks.i_time_subtitle;
+    float delay1 = delay0 + (float)i_additional_subdelay / 1000.f;
+
+    float sub_speed = 1.f / ( (time1 + delay1) - (time0 + delay0) ) * (time1 - time0);
+    float sub_delay = delay0 + (0.f - time0 ) * ( delay1 - delay0 ) / (time1 - time0);
+
+    msg_Dbg(p_demux, "ComputeSubSpeedAndDelay speed=%.2f delay=%.0f",
+            sub_speed, sub_delay);
+
+    /*
+     * We now have a speed ratio candidate.
+     * We then compare it to the list of possible speed ratios, 
+     * and we check if one the ratios is applicable
+     * ==> We will select the closest ratio amongst the applicable ratios
+     */
+    int i;
+    int idx_closest_ratio = -1;
+    float error_closest_ratio = 1E5;
+    /* we allow 33% of user error, i.e if the desired ratio is 
+     * 24 / 25 fps = 0.96
+     * then we allow it to vary between 0.96 +/- 0.33 * (1 - 0.96)
+     * ie between 0.9468 and 0.9732
+     */
+    float max_error_applicable = 0.33;
+    for ( i = 0; i < NB_SUB_FPS_ADJUSTS; i++)
+    {
+        /* do not forget to substract 1 in order to have a usable ratio ! */
+        float ratio_ratio = 
+                ( sub_speed - 1.f) 
+            / ( p_sys->sub_fps_adjusts[i].fps_ratio - 1.f );
+        float error_this_ratio = fabs ( ratio_ratio - 1.f );
+
+        if ( ( error_this_ratio < max_error_applicable) && ( error_this_ratio < error_closest_ratio ) )
+        {
+            error_closest_ratio = error_this_ratio;
+            idx_closest_ratio = i;
+        }
+    }
+
+    if (idx_closest_ratio >= 0)
+    {
+        float spu_delay_applicable = delay0 + (0.f - time0 ) * ( delay1 - delay0 ) / (time1 - time0);
+        out_sub_speed_delay->sub_delay = (int64_t) (spu_delay_applicable  * 1000.f );
+        out_sub_speed_delay->idx_sub_fps_adjust = idx_closest_ratio;
+        return 1;
+    }
+    else
+    {
+        return 0;
+    }
+}
+
+
+/* Returns 1 if a change of delay is possible, 0 otherwise*/
+static int ComputeSubDelay_Only( demux_t *p_demux, struct sub_speed_delay * out_sub_speed_delay )
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+
+    if ( (p_sys->subtitle_delaybookmarks.i_time_audio == 0) || (p_sys->subtitle_delaybookmarks.i_time_subtitle == 0) )
+    {
+        char osd_message[200];
+        snprintf(osd_message, 200, 
+                _("Sub sync: set bookmarks first! (delay=%li %s)"),
+                p_sys->sub_speed_delay_current.sub_delay / 1000,
+                p_sys->sub_fps_adjusts[p_sys->sub_speed_delay_current.idx_sub_fps_adjust].sz_desc
+                );
+        var_SetString(p_demux->p_libvlc, "key-osdmessage", osd_message);
+        return 0;
+    }
+
+    int64_t i_previous_subdelay = GetDemuxSubDelay(p_demux);
+    int64_t i_additional_subdelay = p_sys->subtitle_delaybookmarks.i_time_audio - p_sys->subtitle_delaybookmarks.i_time_subtitle;
+    int64_t i_current_subdelay = i_previous_subdelay + i_additional_subdelay;
+
+    out_sub_speed_delay->sub_delay = i_current_subdelay;
+    out_sub_speed_delay->idx_sub_fps_adjust = p_sys->sub_speed_delay_current.idx_sub_fps_adjust;
+    return 1;
+}
+
+static void Apply_sub_speed_delay(demux_t *p_demux, struct sub_speed_delay speed_delay,
+                                  int enable_osd)
+{
+    char osd_message[200];
+    demux_sys_t *p_sys = p_demux->p_sys;
+    if ( speed_delay.idx_sub_fps_adjust == 0 )
+    {
+        snprintf(
+                osd_message, 200, 
+                _( "Sub sync: corrected, total delay = %li ms"),
+                speed_delay.sub_delay / 1000 
+                );
+    }
+    else
+    {
+        snprintf(
+                osd_message, 200, 
+                _( "Sub sync: corrected, delay = %li ms / %s"),
+                speed_delay.sub_delay / 1000,
+                p_sys->sub_fps_adjusts[speed_delay.idx_sub_fps_adjust].sz_desc
+                );
+    }
+    if (enable_osd)
+        var_SetString(p_demux->p_libvlc, "key-osdmessage", osd_message);
+    
+    p_sys->sub_speed_delay_current = speed_delay;
+    SetDemuxSubDelay(p_demux, speed_delay.sub_delay);
+    set_current_subtitle_by_time(p_demux, p_sys->i_last_demux_date);
+}
+
+static void ApplySubtitleDelay(demux_t *p_demux)
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+    LogDelays(p_demux);
+
+    struct sub_speed_delay speed_delay;
+    
+    if ( 
+            ( p_sys->sub_speed_delay_user_confirm.idx_sub_fps_adjust >= 0)
+         && ( p_sys->subtitle_delaybookmarks.i_time_audio == 0 )
+         && ( p_sys->subtitle_delaybookmarks.i_time_subtitle == 0 )
+        )   
+    {
+        Apply_sub_speed_delay(p_demux, p_sys->sub_speed_delay_user_confirm, 1);
+        p_sys->sub_speed_delay_user_confirm.idx_sub_fps_adjust = -1;
+    }
+    else
+    {
+        p_sys->sub_speed_delay_user_confirm.idx_sub_fps_adjust = -1;
+
+        int enable_osd = 1;
+        if ( ComputeSubSpeedAndDelay( p_demux, &speed_delay ) )
+        {
+            char osd_message[200];
+            snprintf( osd_message, 200,
+                     _("** Press \"Sync subtitles\" (Shift-K) again to correct fps: %s fps **"),
+                     p_sys->sub_fps_adjusts[ speed_delay.idx_sub_fps_adjust ].sz_desc
+                    );
+            var_SetString(p_demux->p_libvlc, "key-osdmessage", osd_message);
+            enable_osd = 0;
+
+            p_sys->sub_speed_delay_user_confirm = speed_delay;
+        }
+
+        if ( ComputeSubDelay_Only( p_demux, &speed_delay ) )
+            Apply_sub_speed_delay(p_demux, speed_delay, enable_osd);
+    }
+    
+    
+    //Store old timestamps in n-1 bookmarks
+    p_sys->subtitle_delaybookmarks.i_time_audio_n_1 = p_sys->subtitle_delaybookmarks.i_time_audio;
+    p_sys->subtitle_delaybookmarks.i_time_subtitle_n_1 = p_sys->subtitle_delaybookmarks.i_time_subtitle;
+    //Clear current bookmarks
+    p_sys->subtitle_delaybookmarks.i_time_audio = 0;
+    p_sys->subtitle_delaybookmarks.i_time_subtitle = 0;
+    
+    Log_speed_delay(p_demux);
+}
+
+
+
+
+
+/*****************************************************************************
  * external callbacks
  *****************************************************************************/
 int subtitle_external_callback ( vlc_object_t * ,char const *, vlc_value_t old_value, vlc_value_t new_value, void * callback_data);
@@ -278,36 +588,23 @@ int subtitle_external_callback (
     }
     if ( ! strcmp(variable_name, "sub-syncbookmarks") )
     {
-        if ( (p_sys->subtitle_delaybookmarks.i_time_audio == 0) || (p_sys->subtitle_delaybookmarks.i_time_subtitle == 0) )
-        {
-            var_SetString(p_demux->p_libvlc, "key-osdmessage", _("Sub sync: set bookmarks first!"));
-        }
-        else
-        {
-            int64_t i_current_subdelay = var_GetTime( p_demux->p_parent, "spu-delay" );
-            int64_t i_additional_subdelay = p_sys->subtitle_delaybookmarks.i_time_audio - p_sys->subtitle_delaybookmarks.i_time_subtitle;
-            int64_t i_total_subdelay = i_current_subdelay + i_additional_subdelay;
-            var_SetTime( p_demux->p_parent, "spu-delay", i_total_subdelay);
-            char message[150];
-            snprintf(message, 150, 
-                    _( "Sub sync: corrected %i ms (total delay = %i ms)" ),
-                                    (int)(i_additional_subdelay / 1000),
-                                    (int)(i_total_subdelay / 1000) );
-            var_SetString(p_demux->p_libvlc, "key-osdmessage", message);
-            p_sys->subtitle_delaybookmarks.i_time_audio = 0;
-            p_sys->subtitle_delaybookmarks.i_time_subtitle = 0;
-        }
+        ApplySubtitleDelay(p_demux);
     }
     if ( ! strcmp(variable_name, "sub-syncreset") )
     {
-        var_SetTime( p_demux->p_parent, "spu-delay", 0);
+        SetDemuxSubDelay(p_demux, 0);
+        p_sys->sub_speed_delay_current.idx_sub_fps_adjust = 0;
         p_sys->subtitle_delaybookmarks.i_time_audio = 0;
         p_sys->subtitle_delaybookmarks.i_time_subtitle = 0;
+        p_sys->subtitle_delaybookmarks.i_time_audio_n_1 = 0;
+        p_sys->subtitle_delaybookmarks.i_time_subtitle_n_1 = 0;
+        set_current_subtitle_by_time(p_demux, p_sys->i_last_demux_date);
         var_SetString(p_demux->p_libvlc, "key-osdmessage", _("Sub sync: delay reset"));
         return VLC_SUCCESS;
     }    
     if ( ! strcmp(variable_name, "spu-delau") )
     {
+        p_sys->sub_speed_delay_current.sub_delay = new_value.i_int;
         set_current_subtitle_by_time(p_demux, p_sys->i_last_demux_date);
     }
     return VLC_SUCCESS;
@@ -339,15 +636,23 @@ static int Open ( vlc_object_t *p_this )
     p_demux->p_sys = p_sys = malloc( sizeof( demux_sys_t ) );
     if( p_sys == NULL )
         return VLC_ENOMEM;
-    
-    /* reset spu-delay at Open*/
-    var_SetTime( p_demux->p_parent, "spu-delay", 0 );
+
+    fill_sub_fps_adjusts(p_demux);
+
+    /* reset spu-delay an speed at Open*/
+    SetDemuxSubDelay(p_demux, 0);
+    p_demux->p_sys->sub_speed_delay_current.idx_sub_fps_adjust = 0;
     /* this is a file subtitle*/
     var_SetInteger(p_demux->p_parent, "sub-isfilesub", 1);
     
     
     p_sys->subtitle_delaybookmarks.i_time_audio = 0;
     p_sys->subtitle_delaybookmarks.i_time_subtitle = 0;
+    p_sys->subtitle_delaybookmarks.i_time_audio_n_1 = -1;
+    p_sys->subtitle_delaybookmarks.i_time_subtitle_n_1 = -1;
+    p_sys->sub_speed_delay_user_confirm.idx_sub_fps_adjust = -1;
+    p_sys->sub_speed_delay_current.sub_delay = 0;
+    p_sys->sub_speed_delay_current.idx_sub_fps_adjust = 0;
 
     
     /* Add callbacks*/
@@ -886,8 +1191,9 @@ static int Demux( demux_t *p_demux )
  *****************************************************************************/
 static int64_t adjust_subtitle_time( demux_t * p_demux, int64_t i64_when)
 {
-    int64_t spu_delay = var_GetTime( p_demux->p_parent, "spu-delay" );
-    int64_t i64_adjust = i64_when + spu_delay;
+    int64_t sub_delay = GetDemuxSubDelay( p_demux );
+    float sub_speed = GetDemuxSubSpeed( p_demux );
+    int64_t i64_adjust = (int64_t) ( (float)i64_when / sub_speed ) + sub_delay;
     return i64_adjust;
 }
 
diff --git a/modules/gui/qt4/components/extended_panels.cpp b/modules/gui/qt4/components/extended_panels.cpp
index 1f36d60..15a0656 100644
--- a/modules/gui/qt4/components/extended_panels.cpp
+++ b/modules/gui/qt4/components/extended_panels.cpp
@@ -1544,7 +1544,23 @@ SyncControls::SyncControls( intf_thread_t *_p_intf, QWidget *_parent ) :
         CONNECT( buttonBookmarkSubtitle, pressed(), this, bookmarkSubtitle() ) ;
 
         QPushButton *buttonBookmarkSync = new QPushButton(_("Sync subtitles"));
-        buttonBookmarkSync->setToolTip(_("You can also use Shift-K on the video"));
+        QString buttonBookmarkSync_Tooltip = qtr( 
+            "You can also use Shift-K on the video\n"\
+            "\n"\
+            "\n"\
+            "If the subtitle has a different fps,\n"\
+            "(ie the delay varies during the movie):\n"\
+            "\n"\
+            "* sync the subtitles at the beginning of the video\n"\
+            "* fast forward a few minutes in the movie\n"\
+            "* then resync the subtitles.\n"\
+            "\n"\
+            "If applicable, you will be asked to change the fps\n"\
+            "of the subtitle.\n"\
+        );
+        buttonBookmarkSync->setToolTip( buttonBookmarkSync_Tooltip );
+        subsVisualSyncLayout->addWidget( buttonBookmarkSync, 0, 2, 1, 1 );
+
         subsVisualSyncLayout->addWidget( buttonBookmarkSync, 0, 2, 1, 1 );
         CONNECT( buttonBookmarkSync, pressed(), this, syncBookmarks() ) ;
 
diff --git a/src/input/var.c b/src/input/var.c
index be3ef2a..8e6eebc 100644
--- a/src/input/var.c
+++ b/src/input/var.c
@@ -202,7 +202,6 @@ void input_ControlVarInit ( input_thread_t *p_input )
     val.i_int = 0;
     var_Change( p_input, "sub-isfilesub", VLC_VAR_SETVALUE, &val, 0 );
 
-    
     /* Video ES */
     var_Create( p_input, "video-es", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
     text.psz_string = _("Video Track");
-- 
1.9.1




More information about the vlc-devel mailing list