[vlc-devel] [PATCH 09/14] Hotkey support for secondary subtitles (Part 1)

Roland Bewick roland.bewick at gmail.com
Sat May 11 08:32:13 CEST 2019


- Toggle control of primary/secondary subtitles
- Adjust Secondary subtitle margin
(NB: Margin is only applied the first time the subtitle is rendered.)
---
 include/vlc_actions.h               |  2 ++
 include/vlc_spu.h                   |  9 +++++++
 modules/control/hotkeys.c           | 23 ++++++++++++++--
 src/input/player.c                  |  7 +++--
 src/libvlc-module.c                 |  6 +++++
 src/misc/actions.c                  |  1 +
 src/video_output/video_output.c     |  4 +--
 src/video_output/vout_internal.h    |  4 +--
 src/video_output/vout_intf.c        | 22 +++++++++++++++-
 src/video_output/vout_subpictures.c | 52 +++++++++++++++++++++----------------
 10 files changed, 99 insertions(+), 31 deletions(-)

diff --git a/include/vlc_actions.h b/include/vlc_actions.h
index 62f0afae53..e8e2224c80 100644
--- a/include/vlc_actions.h
+++ b/include/vlc_actions.h
@@ -191,6 +191,8 @@ typedef enum vlc_action_id {
     ACTIONID_SUBDELAY_UP,
     /* subtitle toggle */
     ACTIONID_SUBTITLE_TOGGLE,
+    /* dual subtitle control */
+    ACTIONID_SUBTITLE_CONTROL_SECONDARY,
     /* subtitle sync */
     ACTIONID_SUBSYNC_MARKAUDIO,
     ACTIONID_SUBSYNC_MARKSUB,
diff --git a/include/vlc_spu.h b/include/vlc_spu.h
index ee6c68f784..92bce26506 100644
--- a/include/vlc_spu.h
+++ b/include/vlc_spu.h
@@ -31,6 +31,15 @@
 extern "C" {
 #endif
 
+/* How many SPU tracks can be selected at once. */
+#define SPU_MAX_CLOCKS         2
+
+/* ID of the primary SPU */
+#define SPU_ID_PRIMARY         1
+
+/* In the future we may support more than one secondary sub track */
+#define SPU_ID_SECONDARY_START 2
+
 /**
  * \defgroup spu Sub-picture channels
  * \ingroup video_output
diff --git a/modules/control/hotkeys.c b/modules/control/hotkeys.c
index b81debb4ea..45065d1b07 100644
--- a/modules/control/hotkeys.c
+++ b/modules/control/hotkeys.c
@@ -36,6 +36,7 @@
 #include <vlc_player.h>
 #include <vlc_playlist.h>
 #include <vlc_actions.h>
+#include <vlc_spu.h>
 #include "math.h"
 
 struct intf_sys_t
@@ -51,6 +52,9 @@ struct intf_sys_t
 
 static void handle_action(intf_thread_t *, vlc_action_id_t);
 
+/* Use standard subtitle hotkeys to control a selected sub track */
+static int i_control_spu_id = SPU_ID_PRIMARY;
+
 /*****************************
  * interface action handling *
  *****************************/
@@ -447,6 +451,17 @@ PLAYER_ACTION_HANDLER(ToggleSubtitle)
     vlc_player_ToggleSubtitle(player);
 }
 
+PLAYER_ACTION_HANDLER(ControlSubtitleSecondary)
+{
+    VLC_UNUSED(action_id);
+
+    if (++i_control_spu_id > SPU_MAX_CLOCKS)
+        i_control_spu_id = SPU_ID_PRIMARY;
+    
+    vlc_player_vout_OSDMessage(player, _("%s subtitle control"),
+        i_control_spu_id > SPU_ID_PRIMARY ? "Secondary" : "Primary");
+}
+
 PLAYER_ACTION_HANDLER(SyncSubtitle)
 {
     switch (action_id)
@@ -762,13 +777,15 @@ VOUT_ACTION_HANDLER(Deinterlace)
 
 VOUT_ACTION_HANDLER(SubtitleDisplay)
 {
+    const char* psz_sub_margin = i_control_spu_id > SPU_ID_PRIMARY ?
+        "secondary-sub-margin" : "sub-margin";
     switch (action_id)
     {
         case ACTIONID_SUBPOS_DOWN:
-            var_DecInteger(vout, "sub-margin");
+            var_DecInteger(vout, psz_sub_margin);
             break;
         case ACTIONID_SUBPOS_UP:
-            var_IncInteger(vout, "sub-margin");
+            var_IncInteger(vout, psz_sub_margin);
             break;
         default:
         {
@@ -858,6 +875,8 @@ static struct vlc_action const actions[] =
     VLC_ACTION_PLAYER(AUDIODELAY_DOWN, SUBDELAY_UP, Delay, true)
     VLC_ACTION_PLAYER(RATE_NORMAL, RATE_FASTER_FINE, Rate, true)
     VLC_ACTION_PLAYER(SUBTITLE_TOGGLE, SUBTITLE_TOGGLE, ToggleSubtitle, true)
+    VLC_ACTION_PLAYER(SUBTITLE_CONTROL_SECONDARY, SUBTITLE_CONTROL_SECONDARY,
+                      ControlSubtitleSecondary, true)
     VLC_ACTION_PLAYER(SUBSYNC_MARKAUDIO, SUBSYNC_RESET, SyncSubtitle, true)
     VLC_ACTION_PLAYER(NAV_ACTIVATE, NAV_RIGHT, Navigate, true)
     VLC_ACTION_PLAYER(VIEWPOINT_FOV_IN, VIEWPOINT_ROLL_ANTICLOCK, Viewpoint, true)
diff --git a/src/input/player.c b/src/input/player.c
index 47298e9fcd..626bb4c721 100644
--- a/src/input/player.c
+++ b/src/input/player.c
@@ -1793,10 +1793,10 @@ vlc_player_input_HandleVoutEvent(struct vlc_player_input *input,
 {
     assert(ev->vout);
 
-    static const char osd_vars[][sizeof("deinterlace-mode")] = {
+    static const char osd_vars[][sizeof("secondary-sub-margin")] = {
         "aspect-ratio", "autoscale", "crop", "crop-bottom",
         "crop-top", "crop-left", "crop-right", "deinterlace",
-        "deinterlace-mode", "sub-margin", "zoom"
+        "deinterlace-mode", "sub-margin", "secondary-sub-margin", "zoom"
     };
 
     vlc_player_t *player = input->player;
@@ -3312,6 +3312,9 @@ vlc_player_VoutOSDCallback(vlc_object_t *this, const char *var,
     else if (strcmp(var, "sub-margin") == 0)
         vouts_osd_Message(&vout, 1, _("Subtitle position %d px"), newval.i_int);
 
+    else if (strcmp(var, "secondary-sub-margin") == 0)
+        vouts_osd_Message(&vout, 1, _("Secondary subtitle position %d px"), newval.i_int);
+
     else if (strcmp(var, "sub-text-scale") == 0)
         vouts_osd_Message(&vout, 1, _("Subtitle text scale %d%%"), newval.i_int);
 
diff --git a/src/libvlc-module.c b/src/libvlc-module.c
index aef485b45a..6c3d44b2cb 100644
--- a/src/libvlc-module.c
+++ b/src/libvlc-module.c
@@ -1454,6 +1454,8 @@ static const char *const mouse_wheel_texts[] = {
 #define SUBTITLE_TRACK_KEY_LONGTEXT N_("Cycle through the available subtitle tracks.")
 #define SUBTITLE_TOGGLE_KEY_TEXT N_("Toggle subtitles")
 #define SUBTITLE_TOGGLE_KEY_LONGTEXT N_("Toggle subtitle track visibility.")
+#define SUBTITLE_CONTROL_SECONDARY_KEY_TEXT N_("Toggle secondary subtitle control")
+#define SUBTITLE_CONTROL_SECONDARY_KEY_LONGTEXT N_("Use original subtitle controls to manage secondary subtitles.")
 #define PROGRAM_SID_NEXT_KEY_TEXT N_("Cycle next program Service ID")
 #define PROGRAM_SID_NEXT_KEY_LONGTEXT N_("Cycle through the available next program Service IDs (SIDs).")
 #define PROGRAM_SID_PREV_KEY_TEXT N_("Cycle previous program Service ID")
@@ -2351,6 +2353,7 @@ vlc_module_begin ()
 #   define KEY_AUDIO_TRACK        "l"
 #   define KEY_SUBTITLE_TRACK     "s"
 #   define KEY_SUBTITLE_TOGGLE    "Shift+s"
+#   define KEY_SUBTITLE_CONTROL_S "Command+Shift+v"
 #   define KEY_SUBTITLE_REVTRACK  "Alt+s"
 #   define KEY_PROGRAM_SID_NEXT   "x"
 #   define KEY_PROGRAM_SID_PREV   "Shift+x"
@@ -2494,6 +2497,7 @@ vlc_module_begin ()
 #   define KEY_AUDIO_TRACK        "b"
 #   define KEY_SUBTITLE_TRACK     "v"
 #   define KEY_SUBTITLE_TOGGLE    "Shift+v"
+#   define KEY_SUBTITLE_CONTROL_S "Ctrl+Shift+v"
 #   define KEY_SUBTITLE_REVTRACK  "Alt+v"
 #   define KEY_PROGRAM_SID_NEXT   "x"
 #   define KEY_PROGRAM_SID_PREV   "Shift+x"
@@ -2663,6 +2667,8 @@ vlc_module_begin ()
             SUBTITLE_TRACK_KEY_TEXT, SUBTITLE_TRACK_KEY_LONGTEXT)
     add_key("key-subtitle-toggle", KEY_SUBTITLE_TOGGLE,
             SUBTITLE_TOGGLE_KEY_TEXT, SUBTITLE_TOGGLE_KEY_LONGTEXT)
+    add_key("key-subtitle-control-secondary", KEY_SUBTITLE_CONTROL_S,
+            SUBTITLE_CONTROL_SECONDARY_KEY_TEXT, SUBTITLE_CONTROL_SECONDARY_KEY_LONGTEXT)
     add_key("key-program-sid-next", KEY_PROGRAM_SID_NEXT,
             PROGRAM_SID_NEXT_KEY_TEXT, PROGRAM_SID_NEXT_KEY_LONGTEXT)
     add_key("key-program-sid-prev", KEY_PROGRAM_SID_PREV,
diff --git a/src/misc/actions.c b/src/misc/actions.c
index e10eb94790..b6e76e75d9 100644
--- a/src/misc/actions.c
+++ b/src/misc/actions.c
@@ -347,6 +347,7 @@ static const struct name2action
     { "subsync-markaudio", ACTIONID_SUBSYNC_MARKAUDIO, },
     { "subsync-marksub", ACTIONID_SUBSYNC_MARKSUB, },
     { "subsync-reset", ACTIONID_SUBSYNC_RESET, },
+    { "subtitle-control-secondary", ACTIONID_SUBTITLE_CONTROL_SECONDARY, },
     { "subtitle-revtrack", ACTIONID_SUBTITLE_REVERSE_TRACK, },
     { "subtitle-text-scale-down", ACTIONID_SUBTITLE_TEXT_SCALE_DOWN, },
     { "subtitle-text-scale-normal", ACTIONID_SUBTITLE_TEXT_SCALE_NORMAL, },
diff --git a/src/video_output/video_output.c b/src/video_output/video_output.c
index 1c8747e6d3..e50140f1b5 100644
--- a/src/video_output/video_output.c
+++ b/src/video_output/video_output.c
@@ -606,14 +606,14 @@ void vout_ControlChangeSubFilters(vout_thread_t *vout, const char *filters)
     vlc_mutex_unlock(&vout->p->spu_lock);
 }
 
-void vout_ChangeSubMargin(vout_thread_t *vout, int margin)
+void vout_ChangeSubMargin(vout_thread_t *vout, int margin, int spu_id)
 {
     assert(!vout->p->dummy);
     if (unlikely(vout->p->spu == NULL))
         return;
 
     vlc_mutex_lock(&vout->p->spu_lock);
-    spu_ChangeMargin(vout->p->spu, margin);
+    spu_ChangeMargin(vout->p->spu, margin, spu_id);
     vlc_mutex_unlock(&vout->p->spu_lock);
 }
 
diff --git a/src/video_output/vout_internal.h b/src/video_output/vout_internal.h
index d60cf7e19b..38e4fc6cf4 100644
--- a/src/video_output/vout_internal.h
+++ b/src/video_output/vout_internal.h
@@ -244,7 +244,7 @@ void vout_ChangeCropBorder(vout_thread_t *, int left, int top, int right, int bo
 void vout_ControlChangeFilters(vout_thread_t *, const char *);
 void vout_ControlChangeSubSources(vout_thread_t *, const char *);
 void vout_ControlChangeSubFilters(vout_thread_t *, const char *);
-void vout_ChangeSubMargin(vout_thread_t *, int);
+void vout_ChangeSubMargin(vout_thread_t *, int, int);
 void vout_ChangeViewpoint( vout_thread_t *, const vlc_viewpoint_t *);
 
 /* */
@@ -269,7 +269,7 @@ void spu_clock_Add(spu_t *, vlc_clock_t *);
 void spu_clock_Remove(spu_t *, vlc_clock_t *);
 void spu_clock_ResetAll(spu_t *);
 void spu_clock_SetDelay(spu_t *spu, vlc_tick_t delay, vlc_clock_t *clock);
-void spu_ChangeMargin(spu_t *, int);
+void spu_ChangeMargin(spu_t *, int, int);
 void spu_SetHighlight(spu_t *, const vlc_spu_highlight_t*);
 
 /**
diff --git a/src/video_output/vout_intf.c b/src/video_output/vout_intf.c
index 238e0c44da..f67ff2cef8 100644
--- a/src/video_output/vout_intf.c
+++ b/src/video_output/vout_intf.c
@@ -41,6 +41,7 @@
 #include <vlc_vout_osd.h>
 #include <vlc_strings.h>
 #include <vlc_charset.h>
+#include <vlc_spu.h>
 #include "vout_internal.h"
 #include "snapshot.h"
 
@@ -74,6 +75,8 @@ static int SubFilterCallback( vlc_object_t *, char const *,
                               vlc_value_t, vlc_value_t, void * );
 static int SubMarginCallback( vlc_object_t *, char const *,
                               vlc_value_t, vlc_value_t, void * );
+static int SecondarySubMarginCallback( vlc_object_t *, char const *,
+                                       vlc_value_t, vlc_value_t, void * );
 static int ViewpointCallback( vlc_object_t *, char const *,
                               vlc_value_t, vlc_value_t, void * );
 
@@ -265,6 +268,11 @@ void vout_CreateVars( vout_thread_t *p_vout )
     var_Create( p_vout, "sub-margin",
                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT | VLC_VAR_ISCOMMAND );
 
+    /* Add secondary-sub-margin variable (dual subtitles) */
+    var_Create( p_vout, "secondary-sub-margin",
+                VLC_VAR_INTEGER | VLC_VAR_DOINHERIT | VLC_VAR_ISCOMMAND );
+    var_AddCallback( p_vout, "secondary-sub-margin", SecondarySubMarginCallback, NULL );
+
     /* Mouse coordinates */
     var_Create( p_vout, "mouse-button-down", VLC_VAR_INTEGER );
     var_Create( p_vout, "mouse-moved", VLC_VAR_COORDS );
@@ -308,12 +316,14 @@ void vout_IntfReinit( vout_thread_t *p_vout )
     var_TriggerCallback( p_vout, "sub-source" );
     var_TriggerCallback( p_vout, "sub-filter" );
     var_TriggerCallback( p_vout, "sub-margin" );
+    var_TriggerCallback( p_vout, "secondary-sub-margin" );
 }
 
 void vout_IntfDeinit(vlc_object_t *obj)
 {
     var_DelCallback(obj, "viewpoint", ViewpointCallback, NULL);
     var_DelCallback(obj, "sub-margin", SubMarginCallback, NULL);
+    var_DelCallback(obj, "secondary-sub-margin", SecondarySubMarginCallback, NULL);
     var_DelCallback(obj, "sub-filter", SubFilterCallback, NULL);
     var_DelCallback(obj, "sub-source", SubSourceCallback, NULL);
     var_DelCallback(obj, "video-filter", VideoFilterCallback, NULL);
@@ -609,7 +619,17 @@ static int SubMarginCallback( vlc_object_t *p_this, char const *psz_cmd,
     vout_thread_t *p_vout = (vout_thread_t *)p_this;
     VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(p_data);
 
-    vout_ChangeSubMargin(p_vout, newval.i_int);
+    vout_ChangeSubMargin(p_vout, newval.i_int, SPU_ID_PRIMARY);
+    return VLC_SUCCESS;
+}
+
+static int SecondarySubMarginCallback( vlc_object_t *p_this, char const *psz_cmd,
+                              vlc_value_t oldval, vlc_value_t newval, void *p_data)
+{
+    vout_thread_t *p_vout = (vout_thread_t *)p_this;
+    VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(p_data);
+
+    vout_ChangeSubMargin(p_vout, newval.i_int, SPU_ID_SECONDARY_START);
     return VLC_SUCCESS;
 }
 
diff --git a/src/video_output/vout_subpictures.c b/src/video_output/vout_subpictures.c
index 4050e9ea09..51bb97b8a9 100644
--- a/src/video_output/vout_subpictures.c
+++ b/src/video_output/vout_subpictures.c
@@ -52,13 +52,6 @@
 /* Number of simultaneous subpictures */
 #define VOUT_MAX_SUBPICTURES (__MAX(VOUT_MAX_PICTURES, SPU_MAX_PREPARE_TIME/5000))
 
-/* ID of the primary SPU. Secondary SPU IDs will be higher. */
-#define SPU_ID_PRIMARY 1
-
-/* How many SPU clocks can be used simultaneously. Secondary
-   SPUs may have different delay therefore require their own clock. */
-#define SPU_MAX_CLOCKS 2
-
 /* Hold of subpicture with converted ts */
 typedef struct {
     subpicture_t *subpicture;
@@ -83,6 +76,15 @@ struct spu_private_t {
     vlc_clock_t *clocks[SPU_MAX_CLOCKS];  /* one for each selected sub track */
     vlc_tick_t   delays[SPU_MAX_CLOCKS];   /* delays applied to clocks above */
 
+    /* Force alignment for secondary subs */
+    int secondary_sub_alignment[SPU_MAX_CLOCKS - 1];
+
+    /* Move the secondary subtites vertically. 
+     * Note: Primary sub margin is applied to all sub tracks and is absolute.
+     * Secondary sub margin is not absolute to enable overlap detection.
+     */
+    atomic_int secondary_sub_margin[SPU_MAX_CLOCKS - 1];
+
     spu_heap_t   heap;
 
     int channel;             /**< number of subpicture channels registered */
@@ -100,9 +102,6 @@ struct spu_private_t {
     atomic_int margin;                 /**< force position of a subpicture */
     video_palette_t palette;              /**< force palette of subpicture */
 
-    int secondary_sub_alignment;   /**< force alignment for secondary subs */
-    int secondary_sub_margin;      /**< move the secondary sub vertically  */
-
     /* Subpiture filters */
     char           *source_chain_current;
     char           *source_chain_update;
@@ -481,12 +480,8 @@ static void SpuAreaFitInside(spu_area_t *area, const spu_area_t *boundary)
 static void SpuRegionPlace(int *x, int *y,
                            const subpicture_t *subpic,
                            const subpicture_region_t *region,
-                           int i_secondary_spu_align_override)
+                           int i_align)
 {
-    int i_align = region->i_align;
-    if (subpic->i_spu_id > SPU_ID_PRIMARY && i_secondary_spu_align_override >= 0)
-        i_align = i_secondary_spu_align_override;
-
     assert(region->i_x != INT_MAX && region->i_y != INT_MAX);
     if (subpic->b_absolute) {
         *x = region->i_x;
@@ -833,14 +828,23 @@ static void SpuRenderRegion(spu_t *spu,
 
     /* Place the picture
      * We compute the position in the rendered size */
+
+    int i_align = region->i_align;
+    if (subpic->i_spu_id > SPU_ID_PRIMARY)
+    {
+        int i_align_override = sys->secondary_sub_alignment[subpic->i_spu_id - SPU_ID_SECONDARY_START];
+        i_align = i_align_override >= 0 ? i_align_override : i_align;
+    }
+
     SpuRegionPlace(&x_offset, &y_offset,
-                   subpic, region, sys->secondary_sub_alignment);
+                   subpic, region, i_align);
 
     if (subpic->i_spu_id > SPU_ID_PRIMARY && !subpic->b_absolute)
     {
         /* Move the secondary subtitles by the secondary margin before overlap detection.
            This way, overlaps will be resolved if they still exist. */
-        y_offset -= spu_invscale_h(atomic_load(&sys->secondary_sub_margin), scale_size);
+        int secondary_margin = atomic_load(&sys->secondary_sub_margin[subpic->i_spu_id - SPU_ID_SECONDARY_START]);
+        y_offset -= spu_invscale_h(secondary_margin, scale_size);
     }
 
     /* Save this position for subtitle overlap support
@@ -853,7 +857,7 @@ static void SpuRenderRegion(spu_t *spu,
     /* Handle overlapping subtitles when possible */
     if (subpic->b_subtitle && !subpic->b_absolute)
         SpuAreaFixOverlap(dst_area, subtitle_area, subtitle_area_count,
-                          region->i_align);
+                          i_align);
 
     /* we copy the area: for the subtitle overlap support we want
      * to only save the area without margin applied */
@@ -1446,10 +1450,10 @@ spu_t *spu_Create(vlc_object_t *object, vout_thread_t *vout)
     sys->scale_yuvp = NULL;
 
     atomic_init(&sys->margin, var_InheritInteger(spu, "sub-margin"));
-    atomic_init(&sys->secondary_sub_margin,
+    atomic_init(&sys->secondary_sub_margin[0],
                 var_InheritInteger(spu, "secondary-sub-margin"));
 
-    sys->secondary_sub_alignment = var_InheritInteger(spu,
+    sys->secondary_sub_alignment[0] = var_InheritInteger(spu,
                                                       "secondary-spu-alignment");
 
     /* Register the default subpicture channel */
@@ -1913,11 +1917,15 @@ void spu_ChangeFilters(spu_t *spu, const char *filters)
     vlc_mutex_unlock(&sys->lock);
 }
 
-void spu_ChangeMargin(spu_t *spu, int margin)
+void spu_ChangeMargin(spu_t *spu, int margin, int spu_id)
 {
     spu_private_t *sys = spu->p;
 
-    atomic_store(&sys->margin, margin);
+    if (spu_id == SPU_ID_PRIMARY)
+        atomic_store(&sys->margin, margin);
+    else
+        atomic_store(&sys->secondary_sub_margin[spu_id - SPU_ID_SECONDARY_START],
+                     margin);
 }
 
 void spu_SetHighlight(spu_t *spu, const vlc_spu_highlight_t *hl)
-- 
2.11.0



More information about the vlc-devel mailing list