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

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


Secondary subtitle track cycling won't overwrite existing primary subtitle selection.

Using the secondary subtitle control hotkey now enables dual subtitles so it doesn't have to be selected in the UI.

Updated OSD message to show secondary subtitle selection
---
 include/vlc_input.h               |  4 ++
 include/vlc_player.h              | 16 ++++++-
 modules/control/hotkeys.c         |  7 +++
 modules/gui/qt/main_interface.cpp |  1 +
 src/input/es_out.c                |  3 ++
 src/input/input.c                 |  3 +-
 src/input/player.c                | 93 +++++++++++++++++++++++++++++++++++----
 src/libvlccore.sym                |  1 +
 8 files changed, 117 insertions(+), 11 deletions(-)

diff --git a/include/vlc_input.h b/include/vlc_input.h
index a5f1eb8b16..20c3ea63e7 100644
--- a/include/vlc_input.h
+++ b/include/vlc_input.h
@@ -461,6 +461,10 @@ struct vlc_input_event_es {
      * ES track information, can be updated after the VLC_INPUT_ES_UPDATED event.
      */
     const es_format_t *fmt;
+    /**
+     * SPU ID for this ES, can be updated after the VLC_INPUT_ES_UPDATED event.
+     */
+    unsigned int spu_id;
 };
 
 struct vlc_input_event_signal {
diff --git a/include/vlc_player.h b/include/vlc_player.h
index ae3c96d30b..7441af596c 100644
--- a/include/vlc_player.h
+++ b/include/vlc_player.h
@@ -111,6 +111,8 @@ struct vlc_player_track
     es_format_t fmt;
     /** True if the track is selected */
     bool selected;
+    /** SPU ID of the track if selected */
+    unsigned int i_spu_id;
 };
 
 /**
@@ -2273,7 +2275,7 @@ VLC_API float
 vlc_player_GetAssociatedSubsFPS(vlc_player_t *player);
 
 /**
- * Enable or disable the abiltity to select multiple sub tracks.
+ * Enable or disable the ability to select multiple sub tracks.
  *
  * @note A successful call will trigger the
  * vlc_player_cbs.on_dual_subtitles_enabled_changed event.
@@ -2468,6 +2470,18 @@ VLC_API void
 vlc_player_SetSubtitleMultipleSelection(vlc_player_t *player, bool multiple);
 
 /**
+ * Cycle sub track matching the given SPU ID.
+ *
+ * @note This must be returned to SPU_ID_PRIMARY immediately after
+ * selection of the subtitle.
+ *
+ * @param player locked player instance
+ * @param select true to allow selection of multiple subtitles
+ */
+VLC_API void
+vlc_player_SetCycleSpuId(vlc_player_t *player, int spu_id);
+
+/**
  * Get the signal quality and strength of the current media
  *
  * @param player locked player instance
diff --git a/modules/control/hotkeys.c b/modules/control/hotkeys.c
index 82ceb3c82e..48571d0725 100644
--- a/modules/control/hotkeys.c
+++ b/modules/control/hotkeys.c
@@ -350,6 +350,8 @@ PLAYER_ACTION_HANDLER(NavigateMedia)
 
 PLAYER_ACTION_HANDLER(Track)
 {
+    vlc_player_SetSubtitleMultipleSelection(player, i_control_spu_id > 1);
+    vlc_player_SetCycleSpuId(player, i_control_spu_id);
     switch (action_id)
     {
         case ACTIONID_AUDIO_TRACK:
@@ -364,6 +366,8 @@ PLAYER_ACTION_HANDLER(Track)
         default:
             vlc_assert_unreachable();
     }
+    vlc_player_SetCycleSpuId(player, SPU_ID_PRIMARY);
+    vlc_player_SetSubtitleMultipleSelection(player, false);
 }
 
 PLAYER_ACTION_HANDLER(Delay)
@@ -459,6 +463,9 @@ PLAYER_ACTION_HANDLER(ControlSubtitleSecondary)
     if (++i_control_spu_id > SPU_MAX_CLOCKS)
         i_control_spu_id = SPU_ID_PRIMARY;
     
+    if (i_control_spu_id > SPU_ID_PRIMARY)
+        vlc_player_SetDualSubtitlesEnabled(player, true);
+    
     vlc_player_vout_OSDMessage(player, _("%s subtitle control"),
         i_control_spu_id > SPU_ID_PRIMARY ? "Secondary" : "Primary");
 }
diff --git a/modules/gui/qt/main_interface.cpp b/modules/gui/qt/main_interface.cpp
index 58e81d8d6e..8f1f890b0b 100644
--- a/modules/gui/qt/main_interface.cpp
+++ b/modules/gui/qt/main_interface.cpp
@@ -205,6 +205,7 @@ MainInterface::MainInterface( intf_thread_t *_p_intf ) : QVLCMW( _p_intf ),
         connect( THEMIM, &PlayerController::nameChanged, this, &MainInterface::setVLCWindowsTitle );
     }
     connect( THEMIM, &PlayerController::inputChanged, this, &MainInterface::onInputChanged );
+    connect( THEMIM, &PlayerController::dualSubtitlesEnabledChanged, this, &MainInterface::setDualSubtitlesEnabled);
 
     /* END CONNECTS ON IM */
 
diff --git a/src/input/es_out.c b/src/input/es_out.c
index 0a26c7680b..1480ffbeba 100644
--- a/src/input/es_out.c
+++ b/src/input/es_out.c
@@ -1061,6 +1061,8 @@ static void EsOutSendEsEvent(es_out_t *out, es_out_id_t *es, int action)
         .id = &es->id,
         .title = es->psz_title ? es->psz_title : "",
         .fmt = es->fmt_out.i_cat != UNKNOWN_ES ? &es->fmt_out : &es->fmt,
+        .spu_id = es->fmt.i_cat == SPU_ES && es->p_dec ?
+                  input_DecoderGetSpuId(es->p_dec) : SPU_ID_PRIMARY,
     });
 }
 
@@ -2251,6 +2253,7 @@ static void EsOutSelect( es_out_t *out, es_out_id_t *es, bool b_force )
     {
         input_DecoderSetSpuId( es->p_dec, EsOutCountSelected( p_sys, es->fmt.i_cat ) );
         EsOutDecoderChangeDelay(out, es);
+        EsOutSendEsEvent( out, es, VLC_INPUT_ES_UPDATED );
     }
 }
 
diff --git a/src/input/input.c b/src/input/input.c
index d380736daa..a18fa69f7c 100644
--- a/src/input/input.c
+++ b/src/input/input.c
@@ -1062,7 +1062,8 @@ static void SetSubtitlesOptions( input_thread_t *p_input )
     if( sub_delay != 0 )
     {
         priv->spu_delays[0] = vlc_tick_from_samples(sub_delay, 10);
-        input_SendEventSubtitleDelay( p_input, priv->spu_delays[0], false );
+        input_SendEventSubtitleDelay( p_input, priv->spu_delays[0],
+                                      SPU_ID_PRIMARY );
         /* UpdatePtsDelay will be called next by InitPrograms */
     }
 
diff --git a/src/input/player.c b/src/input/player.c
index e66fc9552d..9a15ecf51a 100644
--- a/src/input/player.c
+++ b/src/input/player.c
@@ -133,6 +133,9 @@ struct vlc_player_input
     bool teletext_transparent;
     unsigned teletext_page;
 
+    bool b_select_secondary_subtitle;
+    unsigned int i_cycle_spu_id;
+
     struct
     {
         vlc_tick_t time;
@@ -432,6 +435,7 @@ vlc_player_track_New(vlc_es_id_t *id, const char *name, const es_format_t *fmt)
     }
     track->es_id = vlc_es_id_Hold(id);
     track->selected = false;
+    track->i_spu_id = SPU_ID_PRIMARY;
 
     return track;
 }
@@ -459,7 +463,8 @@ vlc_player_track_Delete(struct vlc_player_track *track)
 
 static int
 vlc_player_track_Update(struct vlc_player_track *track,
-                        const char *name, const es_format_t *fmt)
+                        const char *name, const es_format_t *fmt,
+                        unsigned int spu_id)
 {
     if (strcmp(name, track->name) != 0)
     {
@@ -477,6 +482,8 @@ vlc_player_track_Update(struct vlc_player_track *track,
 
     es_format_Clean(&track->fmt);
     track->fmt = fmtdup;
+
+    track->i_spu_id = spu_id;
     return VLC_SUCCESS;
 }
 
@@ -659,6 +666,9 @@ vlc_player_input_New(vlc_player_t *player, input_item_t *item)
     input->subsync.audio_time =
     input->subsync.subtitle_time = VLC_TICK_INVALID;
 
+    input->b_select_secondary_subtitle = false;
+    input->i_cycle_spu_id = SPU_ID_PRIMARY;
+
     vlc_vector_init(&input->program_vector);
     vlc_vector_init(&input->video_track_vector);
     vlc_vector_init(&input->audio_track_vector);
@@ -1294,7 +1304,24 @@ es_format_category_to_string(enum es_format_category_e cat)
 static void
 vlc_player_vout_OSDTrack(vlc_player_t *player, vlc_es_id_t *id, bool select)
 {
+    struct vlc_player_input *input = vlc_player_get_input_locked(player);
     enum es_format_category_e cat = vlc_es_id_GetCat(id);
+    size_t spu_selected_count = 0;
+
+    if (cat == SPU_ES && input && var_GetBool(input->thread, "multiple-spus") &&
+        input->b_select_secondary_subtitle)
+    {
+        size_t count = vlc_player_GetTrackCount(player, SPU_ES);
+        for (size_t i = 0; i < count; ++i)
+        {
+            const struct vlc_player_track *track =
+                vlc_player_GetTrackAt(player, SPU_ES, i);
+            assert(track);
+            if (track->selected && track->es_id != id)
+                ++spu_selected_count;
+        }
+    }
+
     const struct vlc_player_track *track = vlc_player_GetTrack(player, id);
     if (!track && select)
         return;
@@ -1302,7 +1329,10 @@ vlc_player_vout_OSDTrack(vlc_player_t *player, vlc_es_id_t *id, bool select)
     const char *cat_name = es_format_category_to_string(cat);
     assert(cat_name);
     const char *track_name = select ? track->name : _("N/A");
-    vlc_player_vout_OSDMessage(player, _("%s track: %s"), cat_name, track_name);
+    bool b_track_secondary = spu_selected_count > 0;
+    const char *track_secondary = b_track_secondary ? _("Secondary ") : _("");
+    vlc_player_vout_OSDMessage(player, _("%s%s track: %s"),
+                               track_secondary, cat_name, track_name);
 }
 
 void
@@ -1320,6 +1350,9 @@ static void
 vlc_player_CycleTrack(vlc_player_t *player, enum es_format_category_e cat,
                       bool next)
 {
+    struct vlc_player_input *input = vlc_player_get_input_locked(player);
+    unsigned int i_cycle_spu_id = input ? input->i_cycle_spu_id : SPU_ID_PRIMARY;
+
     size_t count = vlc_player_GetTrackCount(player, cat);
     if (!count)
         return;
@@ -1331,12 +1364,12 @@ vlc_player_CycleTrack(vlc_player_t *player, enum es_format_category_e cat,
         const struct vlc_player_track *track =
             vlc_player_GetTrackAt(player, cat, i);
         assert(track);
-        if (track->selected)
+        if (track->selected && (cat != SPU_ES || track->i_spu_id == i_cycle_spu_id))
         {
             if (selected)
             {
                 /* Can't cycle through tracks if there are more than one
-                 * selected */
+                 * selected for the same spu id */
                 return;
             }
             index = i;
@@ -1344,19 +1377,51 @@ vlc_player_CycleTrack(vlc_player_t *player, enum es_format_category_e cat,
         }
     }
 
+    size_t original_index = index;
+    bool was_selected = selected;
+    bool b_check_first = false;
+
     if (!selected)
     {
-        /* No track selected: select the first or the last track */
+        /* No track selected: try to select the first or the last track. */
         index = next ? 0 : count - 1;
+        b_check_first = true;
         selected = true;
     }
-    else
+
+    /* Search until we find an unselected track */
+    while (true)
     {
         /* Unselect if we reach the end of the cycle */
         if ((next && index + 1 == count) || (!next && index == 0))
+        {
+            if (was_selected)
+                index = original_index;
             selected = false;
-        else /* Switch to the next or previous track */
-            index = index + (next ? 1 : -1);
+            break;
+        }
+        else /* Switch to the next or previous unselected track */
+        {
+            if (!b_check_first)
+                index = index + (next ? 1 : -1);
+            else
+                b_check_first = false;
+            const struct vlc_player_track *track =
+                vlc_player_GetTrackAt(player, cat, index);
+            if (!track->selected)
+                break;
+        }
+    }
+
+    if (!selected && !was_selected)
+        return; /* no track available nor selected matching request */
+
+    if (i_cycle_spu_id > SPU_ID_PRIMARY && selected && was_selected)
+    {
+        /* Deselect the old secondary sub track so we can select a new one */
+        const struct vlc_player_track *track =
+            vlc_player_GetTrackAt(player, cat, original_index);
+        vlc_player_UnselectTrack(player, track->es_id);
     }
 
     const struct vlc_player_track *track =
@@ -1584,7 +1649,7 @@ vlc_player_input_HandleEsEvent(struct vlc_player_input *input,
             track = vlc_player_track_vector_FindById(vec, ev->id, NULL);
             if (!track)
                 break;
-            if (vlc_player_track_Update(track, ev->title, ev->fmt) != 0)
+            if (vlc_player_track_Update(track, ev->title, ev->fmt, ev->spu_id) != 0)
                 break;
             vlc_player_SendEvent(player, on_track_list_changed,
                                  VLC_PLAYER_LIST_UPDATED, track);
@@ -2921,11 +2986,21 @@ vlc_player_SetSubtitleMultipleSelection(vlc_player_t *player, bool multiple)
     struct vlc_player_input *input = vlc_player_get_input_locked(player);
     if (!input)
         return;
+    input->b_select_secondary_subtitle = multiple;
     input_ControlPushHelper(input->thread,
                             INPUT_CONTROL_SET_MULTIPLE_SUBTITLE_SELECTION,
                             &(vlc_value_t){ .b_bool = multiple });
 }
 
+void
+vlc_player_SetCycleSpuId(vlc_player_t *player, int spu_id)
+{
+    struct vlc_player_input *input = vlc_player_get_input_locked(player);
+    if (!input)
+        return;
+    input->i_cycle_spu_id = spu_id;
+}
+
 vlc_tick_t
 vlc_player_GetSubtitleDelay(vlc_player_t *player, int i_spu_id)
 {
diff --git a/src/libvlccore.sym b/src/libvlccore.sym
index 175d9a9639..baef0ba9dd 100644
--- a/src/libvlccore.sym
+++ b/src/libvlccore.sym
@@ -869,6 +869,7 @@ vlc_player_SetSubtitleDelay
 vlc_player_SetSubtitleSync
 vlc_player_SetSubtitleTextScale
 vlc_player_SetSubtitleMultipleSelection
+vlc_player_SetCycleSpuId
 vlc_player_SetDualSubtitlesEnabled
 vlc_player_SetTeletextEnabled
 vlc_player_SetTeletextTransparency
-- 
2.11.0



More information about the vlc-devel mailing list