[vlc-devel] [PATCH 01/15] Enable selection of secondary SPU ES in the core

Roland Bewick roland.bewick at gmail.com
Thu May 16 15:40:39 CEST 2019


Hi,

I've chosen the following implementation for vlc_player_SelectTrackList:
1. Deselect all selected tracks not matching the provided list.
2. Sequentially select the tracks that aren't already selected.

When a track is selected in the QT GUI, I check the category of the track.
- If Not SPU_ES, use vlc_player_SelectTrack as usual.
- If SPU_ES:
     1. find which tracks are already selected by looping through all 
tracks of the same category
     2. If more than one track is already selected, do not proceed with 
the selection
     3. Call vlc_player_SelectTrackList using the ids of the new track 
and the already selected tracks

To ensure that multiple tracks get selected in the core (es_out.c) I've 
added a new parameter to EsOutSelect.

Please let me know if you have any comments.
Thanks,
Roland

Diff:

diff --git a/include/vlc_es_out.h b/include/vlc_es_out.h
index b690d570e9..ead0e7a0e0 100644
--- a/include/vlc_es_out.h
+++ b/include/vlc_es_out.h
@@ -35,9 +35,10 @@
  enum es_out_query_e
  {
      /* set or change the selected ES in its category (audio/video/spu) */
-    ES_OUT_SET_ES,      /* arg1= es_out_id_t*                   */
-    ES_OUT_UNSET_ES,    /* arg1= es_out_id_t* res=can fail      */
-    ES_OUT_RESTART_ES,  /* arg1= es_out_id_t*                   */
+    ES_OUT_SET_ES,          /* arg1= es_out_id_t* arg2=bool      */
+    ES_OUT_SET_ES_MULTIPLE, /* arg1= int, arg2=bool              */
+    ES_OUT_UNSET_ES,        /* arg1= es_out_id_t* res=can fail   */
+    ES_OUT_RESTART_ES,      /* arg1= es_out_id_t*                */

      /* set 'default' tag on ES (copied across from container) */
      ES_OUT_SET_ES_DEFAULT, /* arg1= es_out_id_t*                */
diff --git a/include/vlc_player.h b/include/vlc_player.h
index 1aa5f8e687..aa641a712e 100644
--- a/include/vlc_player.h
+++ b/include/vlc_player.h
@@ -1675,6 +1675,32 @@ vlc_player_GetSelectedTrack(vlc_player_t *player, 
enum es_format_category_e cat)
  VLC_API void
  vlc_player_SelectTrack(vlc_player_t *player, vlc_es_id_t *es_id);

+
+/**
+ * Select multiple tracks from a list of ES identifiers. Any currently
+ * selected track not referenced in the list will be unselected.
+ *
+ * @warning there is no guarantee all requested tracks will be selected.
+ * Only one audio track can be selected at a time. Two subtitle tracks
+ * can be selected simultaneously if the dual subtitles setting is enabled.
+ *
+ * @warning behaviour is undefined if the list is not null-terminated.
+ *
+ * @note A successful call will trigger the
+ * vlc_player_cbs.on_track_selection_changed event for each track that has
+ * its selection state changed.
+ *
+ * @param player locked player instance
+ * @param cat VIDEO_ES, AUDIO_ES or SPU_ES
+ * @param es_id_list a null-terminated list of ES identifiers
+ * (ES IDs can be retrieved from vlc_player_cbs.on_track_list_changed or
+ * vlc_player_GetTrackAt())
+ */
+VLC_API void
+vlc_player_SelectTrackList(vlc_player_t *player,
+                           enum es_format_category_e cat,
+                           vlc_es_id_t *const es_id_list[]);
+
  /**
   * Select the next track
   *
diff --git a/modules/gui/qt/util/input_models.cpp 
b/modules/gui/qt/util/input_models.cpp
index e381fbb917..857cfee972 100644
--- a/modules/gui/qt/util/input_models.cpp
+++ b/modules/gui/qt/util/input_models.cpp
@@ -62,7 +62,47 @@ bool TrackListModel::setData(const QModelIndex 
&index, const QVariant &value, in
      vlc_player_locker lock{ m_player };

      if (select)
-        vlc_player_SelectTrack(m_player, m_data[row].m_id.get());
+    {
+        vlc_es_id_t *esId = m_data[row].m_id.get();
+        const struct vlc_player_track *trackToSelect = 
vlc_player_GetTrack(m_player, esId);
+        assert(trackToSelect);
+
+        if (trackToSelect->fmt.i_cat != SPU_ES)
+        {
+            /* For now, use existing behaviour for video/audio tracks */
+            vlc_player_SelectTrack(m_player, esId);
+        }
+        else
+        {
+            /* Try to select a maximum of two tracks. */
+            const int maxIds = 2;
+            enum es_format_category_e cat = trackToSelect->fmt.i_cat;
+            vlc_es_id_t *esIds[maxIds + 1] = { esId }; /* null 
terminated */
+
+            size_t count = vlc_player_GetTrackCount(m_player, cat);
+            if (!count)
+                return false;
+
+            unsigned int index = 1;
+            for (size_t i = 0; i < count; ++i)
+            {
+                const struct vlc_player_track *track =
+                    vlc_player_GetTrackAt(m_player, SPU_ES, i);
+                assert(track);
+
+                if (track->selected)
+                {
+                    if (index == maxIds)
+                    {
+ updateTrackSelection(trackToSelect->es_id, false);
+                        return false;
+                    }
+                    esIds[index++] = track->es_id;
+                }
+            }
+            vlc_player_SelectTrackList(m_player, cat, esIds);
+        }
+    }
      else
          vlc_player_UnselectTrack(m_player, m_data[row].m_id.get());
      return true;
diff --git a/src/input/es_out.c b/src/input/es_out.c
index 2026fb8c22..4523efce54 100644
--- a/src/input/es_out.c
+++ b/src/input/es_out.c
@@ -216,7 +216,7 @@ static void         EsOutDelLocked( es_out_t *, 
es_out_id_t * );
  static void         EsOutDel    ( es_out_t *, es_out_id_t * );

  static void         EsOutTerminate( es_out_t * );
-static void         EsOutSelect( es_out_t *, es_out_id_t *es, bool 
b_force );
+static void         EsOutSelect( es_out_t *, es_out_id_t *es, bool 
b_force, bool b_multiple );
  static void         EsOutUpdateInfo( es_out_t *, es_out_id_t *es, 
const vlc_meta_t * );
  static int          EsOutSetRecord(  es_out_t *, bool b_record );

@@ -1114,7 +1114,7 @@ static void EsOutProgramSelect( es_out_t *out, 
es_out_pgrm_t *p_pgrm )
              EsOutUpdateInfo(out, es, NULL);
          }

-        EsOutSelect(out, es, false);
+        EsOutSelect(out, es, false, false);
      }

      /* Ensure the correct running EPG table is selected */
@@ -1774,7 +1774,7 @@ static es_out_id_t *EsOutAddSlaveLocked( es_out_t 
*out, const es_format_t *fmt,
          EsOutSendEsEvent( out, es, VLC_INPUT_ES_ADDED );

      EsOutUpdateInfo( out, es, NULL );
-    EsOutSelect( out, es, false );
+    EsOutSelect( out, es, false, false );

      if( es->b_scrambled )
          EsOutProgramUpdateScrambled( out, es->p_pgrm );
@@ -2043,9 +2043,11 @@ static void EsOutUnselectEs( es_out_t *out, 
es_out_id_t *es, bool b_update )
   * \param out The es_out structure
   * \param es es_out_id structure
   * \param b_force ...
+ * \param b_multiple preserve currently selected ES
   * \return nothing
   */
-static void EsOutSelect( es_out_t *out, es_out_id_t *es, bool b_force )
+static void EsOutSelect( es_out_t *out, es_out_id_t *es, bool b_force,
+                         bool b_multiple )
  {
      es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
      es_out_es_props_t *p_esprops = GetPropsByCat( p_sys, es->fmt.i_cat );
@@ -2058,7 +2060,8 @@ static void EsOutSelect( es_out_t *out, 
es_out_id_t *es, bool b_force )

      bool b_auto_unselect = p_esprops && p_sys->i_mode == 
ES_OUT_MODE_AUTO &&
                             p_esprops->e_policy == 
ES_OUT_ES_POLICY_EXCLUSIVE &&
-                           p_esprops->p_main_es && p_esprops->p_main_es 
!= es;
+                           p_esprops->p_main_es && p_esprops->p_main_es 
!= es &&
+                           !b_multiple;

      if( p_sys->i_mode == ES_OUT_MODE_ALL || b_force )
      {
@@ -2218,7 +2221,7 @@ static void EsOutCreateCCChannels( es_out_t *out, 
vlc_fourcc_t codec, uint64_t i

          /* Enable if user specified on command line */
          if (p_sys->sub.i_channel == i)
-            EsOutSelect(out, *pp_es, true);
+            EsOutSelect(out, *pp_es, true, false);
      }
  }

@@ -2410,7 +2413,7 @@ static void EsOutDelLocked( es_out_t *out, 
es_out_id_t *es )
                          p_esprops->p_main_es = other;
                  }
                  else
-                    EsOutSelect(out, other, false);
+                    EsOutSelect(out, other, false, false);
              }
      }

@@ -2532,7 +2535,7 @@ static int EsOutVaControlLocked( es_out_t *out, 
int i_query, va_list args )
          }
          foreach_es_then_es_slaves(es)
          {
-            EsOutSelect(out, es, false);
+            EsOutSelect(out, es, false, false);
          }

          if( i_mode == ES_OUT_MODE_END )
@@ -2540,6 +2543,7 @@ static int EsOutVaControlLocked( es_out_t *out, 
int i_query, va_list args )
          return VLC_SUCCESS;
      }

+    case ES_OUT_SET_ES_MULTIPLE:
      case ES_OUT_SET_ES:
      case ES_OUT_RESTART_ES:
      {
@@ -2573,9 +2577,11 @@ static int EsOutVaControlLocked( es_out_t *out, 
int i_query, va_list args )
                          EsOutDestroyDecoder(out, es);
                          EsOutCreateDecoder(out, es);
                      }
-                    else if( i_query == ES_OUT_SET_ES )
+                    else if( i_query == ES_OUT_SET_ES ||
+                             i_query == ES_OUT_SET_ES_MULTIPLE )
                      {
-                        EsOutSelect(out, es, true);
+                        bool b_multiple = i_query == 
ES_OUT_SET_ES_MULTIPLE;
+                        EsOutSelect(out, es, true, b_multiple);
                      }
                      break;
                  }
diff --git a/src/input/es_out_timeshift.c b/src/input/es_out_timeshift.c
index 406c6f2237..ddec6fbec3 100644
--- a/src/input/es_out_timeshift.c
+++ b/src/input/es_out_timeshift.c
@@ -738,6 +738,7 @@ static int ControlLocked( es_out_t *p_out, int 
i_query, va_list args )

      /* Invalid queries for this es_out level */
      case ES_OUT_SET_ES_BY_ID:
+    case ES_OUT_SET_ES_MULTIPLE:
      case ES_OUT_RESTART_ES_BY_ID:
      case ES_OUT_SET_ES_DEFAULT_BY_ID:
      case ES_OUT_GET_ES_OBJECTS_BY_ID:
diff --git a/src/input/input.c b/src/input/input.c
index 381b85a9fd..0f9772211c 100644
--- a/src/input/input.c
+++ b/src/input/input.c
@@ -1708,6 +1708,7 @@ static void ControlRelease( int i_type, const 
input_control_param_t *p_param )
              vlc_renderer_item_release( p_param->val.p_address );
          break;
      case INPUT_CONTROL_SET_ES:
+    case INPUT_CONTROL_SET_ES_MULTIPLE:
      case INPUT_CONTROL_UNSET_ES:
      case INPUT_CONTROL_RESTART_ES:
          vlc_es_id_Release( p_param->id );
@@ -2124,12 +2125,17 @@ static bool Control( input_thread_t *p_input,
                              ES_OUT_RESTART_ES_BY_ID, 
(int)param.val.i_int );
              break;

-        case INPUT_CONTROL_SET_ES:
+        case INPUT_CONTROL_SET_ES: case INPUT_CONTROL_SET_ES_MULTIPLE:
+        {
+            int i_query = i_type == INPUT_CONTROL_SET_ES ?
+                          ES_OUT_SET_ES : ES_OUT_SET_ES_MULTIPLE;
+
              if( es_out_Control( input_priv(p_input)->p_es_out_display,
-                                ES_OUT_SET_ES, param.id ) == VLC_SUCCESS )
+                                i_query, param.id ) == VLC_SUCCESS )
                  demux_Control( input_priv(p_input)->master->p_demux, 
DEMUX_SET_ES,
                                 vlc_es_id_GetInputId( param.id ) );
              break;
+        }
          case INPUT_CONTROL_UNSET_ES:
              es_out_Control( input_priv(p_input)->p_es_out_display,
                              ES_OUT_UNSET_ES, param.id );
@@ -3425,7 +3431,7 @@ static int input_SlaveSourceAdd( input_thread_t 
*p_input,
      es_out_Control( priv->p_es_out_display, ES_OUT_SET_ES_DEFAULT_BY_ID,
                      priv->i_last_es_id );
      es_out_Control( priv->p_es_out_display, ES_OUT_SET_ES_BY_ID,
-                    priv->i_last_es_id, false );
+                    priv->i_last_es_id, false, false );

      return VLC_SUCCESS;
  }
diff --git a/src/input/input_internal.h b/src/input/input_internal.h
index 18495881c2..1bb1ffe091 100644
--- a/src/input/input_internal.h
+++ b/src/input/input_internal.h
@@ -247,6 +247,7 @@ enum input_control_e
      INPUT_CONTROL_RESTART_ES_BY_ID,

      INPUT_CONTROL_SET_ES,
+    INPUT_CONTROL_SET_ES_MULTIPLE,  // select without deselecting other ES
      INPUT_CONTROL_UNSET_ES,
      INPUT_CONTROL_RESTART_ES,

@@ -294,7 +295,8 @@ static inline void input_ControlPushEsHelper( 
input_thread_t *p_input, int i_typ
vlc_es_id_t *id )
  {
      assert( i_type == INPUT_CONTROL_SET_ES || i_type == 
INPUT_CONTROL_UNSET_ES ||
-            i_type == INPUT_CONTROL_RESTART_ES );
+            i_type == INPUT_CONTROL_RESTART_ES ||
+            i_type == INPUT_CONTROL_SET_ES_MULTIPLE );
      input_ControlPush( p_input, i_type, &(input_control_param_t) {
          .id = vlc_es_id_Hold( id ),
      } );
diff --git a/src/input/player.c b/src/input/player.c
index da02375a66..a896d5285d 100644
--- a/src/input/player.c
+++ b/src/input/player.c
@@ -1312,6 +1312,67 @@ vlc_player_SelectTrack(vlc_player_t *player, 
vlc_es_id_t *id)
      vlc_player_vout_OSDTrack(player, id, true);
  }

+void
+vlc_player_SelectTrackList(vlc_player_t *player,
+                           enum es_format_category_e cat,
+                           vlc_es_id_t *const es_id_list[])
+{
+    struct vlc_player_input *input = vlc_player_get_input_locked(player);
+    if (!input)
+        return;
+
+    /* First, deselect any currently selected tracks not in es_id_list */
+    size_t count = vlc_player_GetTrackCount(player, cat);
+    if (!count)
+        return;
+
+    for (size_t i = 0; i < count; ++i)
+    {
+        const struct vlc_player_track *track =
+            vlc_player_GetTrackAt(player, cat, i);
+        assert(track);
+        if (track->selected)
+        {
+            bool deselect = true;
+            for (size_t j = 0; ; j++)
+            {
+                vlc_es_id_t *es_id = es_id_list[j];
+                if (es_id == NULL)
+                    break;
+
+                if (es_id == track->es_id)
+                {
+                    deselect = false;
+                    break;
+                }
+            }
+            if (deselect)
+                vlc_player_UnselectTrack(player, track->es_id);
+        }
+    }
+
+    /* Second, Select each track sequentially.
+        Already selected tracks will be ignored */
+
+    for (size_t j = 0; ; j++)
+    {
+        vlc_es_id_t *es_id = es_id_list[j];
+        if (es_id == NULL)
+            break;
+
+        const struct vlc_player_track *track = 
vlc_player_GetTrack(player, es_id);
+        assert(track);
+
+        if (!track->selected)
+        {
+            input_ControlPushEsHelper(input->thread,
+ INPUT_CONTROL_SET_ES_MULTIPLE,
+                                      es_id);
+            vlc_player_vout_OSDTrack(player, es_id, true);
+        }
+    }
+}
+
  static void
  vlc_player_CycleTrack(vlc_player_t *player, enum es_format_category_e cat,
                        bool next)
diff --git a/src/libvlccore.sym b/src/libvlccore.sym
index efc07fbb25..03a503e1ea 100644
--- a/src/libvlccore.sym
+++ b/src/libvlccore.sym
@@ -856,6 +856,7 @@ vlc_player_SelectTeletextPage
  vlc_player_SelectTitle
  vlc_player_SelectTitleIdx
  vlc_player_SelectTrack
+vlc_player_SelectTrackList
  vlc_player_SetAssociatedSubsFPS
  vlc_player_SetAtoBLoop
  vlc_player_SetAudioDelay





More information about the vlc-devel mailing list