[vlc-devel] [PATCH 1/4] es_out: add controls to cycle through ES tracks

Thomas Guillem thomas at gllm.fr
Wed Nov 14 17:50:15 CET 2018


With 2 new internal controls: ES_OUT_SET_ES_NEXT and ES_OUT_SET_ES_PREV.
This will fix a TOCTOU issue when selecting next or previous tracks.
---
 src/input/es_out.c           | 96 ++++++++++++++++++++++++++++++++++++
 src/input/es_out.h           |  3 ++
 src/input/es_out_timeshift.c |  2 +
 3 files changed, 101 insertions(+)

diff --git a/src/input/es_out.c b/src/input/es_out.c
index 0cdd08cfe2..2960374175 100644
--- a/src/input/es_out.c
+++ b/src/input/es_out.c
@@ -2373,6 +2373,94 @@ static int EsOutControlLocked( es_out_t *out, int i_query, ... )
     return ret;
 }
 
+static int EsOutControlCycleEsLocked(es_out_t *out, enum es_format_category_e cat,
+                                     bool next, vlc_es_id_t **new_id)
+{
+    es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
+    es_out_id_t *es, *selected_es = NULL, *next_es = NULL;
+
+    es_out_es_props_t *props = GetPropsByCat(p_sys, cat);
+    /* Can't cycle through ES if we are not in exclusive mode */
+    if (!props || props->e_policy != ES_OUT_ES_POLICY_EXCLUSIVE)
+        return VLC_EGENERIC;
+
+    /* Get the selected ES */
+    foreach_es_then_es_slaves(es)
+    {
+        if (es->fmt.i_cat != cat || !EsIsSelected(es))
+            continue;
+
+        if (selected_es == NULL)
+            selected_es = es;
+        else
+        {
+            /* Can't select a next/prev track if there are more than
+             * one ES selected */
+            return VLC_EGENERIC;
+        }
+    }
+
+    if (selected_es)
+    {
+        /* Find the closest ES that is after or before the current one */
+        size_t selected_pos = selected_es->i_pos;
+        foreach_es_then_es_slaves(es)
+        {
+            if (es->fmt.i_cat != cat || EsIsSelected(es))
+                continue;
+            assert(es->i_pos != selected_pos);
+
+            if ((next && es->i_pos < selected_pos) ||
+                (!next && es->i_pos > selected_pos))
+                continue;
+
+            if (next_es == NULL)
+            {
+                next_es = es;
+                continue;
+            }
+
+            const int distance = abs(es->i_pos - selected_pos);
+            const int closest_distance = abs(next_es->i_pos - selected_pos);
+            if (distance < closest_distance)
+                next_es = es;
+        }
+    }
+    else
+    {
+        /* No ES currently selected: find the first or last ES to select */
+        foreach_es_then_es_slaves(es)
+        {
+            if (es->fmt.i_cat != cat)
+                continue;
+            if (next_es == NULL)
+            {
+                next_es = es;
+                continue;
+            }
+            if ((next && es->i_pos < next_es->i_pos) ||
+                (!next && es->i_pos > next_es->i_pos))
+                next_es = es;
+        }
+    }
+
+    if (next_es)
+    {
+        int ret = EsOutControlLocked(out, ES_OUT_SET_ES, next_es);
+        if (ret == VLC_SUCCESS)
+            *new_id = &next_es->id;
+        return ret;
+    }
+    else
+    {
+        assert(selected_es);
+        int ret = EsOutControlLocked(out, ES_OUT_UNSET_ES, selected_es);
+        if (ret == VLC_SUCCESS)
+            *new_id = NULL;
+        return ret;
+    }
+}
+
 /**
  * Control query handler
  *
@@ -2537,6 +2625,14 @@ static int EsOutVaControlLocked( es_out_t *out, int i_query, va_list args )
 
         return VLC_SUCCESS;
     }
+    case ES_OUT_SET_ES_NEXT:
+    case ES_OUT_SET_ES_PREV:
+    {
+        const enum es_format_category_e cat = va_arg(args, int);
+        vlc_es_id_t **new_id = va_arg(args, vlc_es_id_t **);
+        return EsOutControlCycleEsLocked(out, cat,
+                                         i_query == ES_OUT_SET_ES_NEXT, new_id);
+    }
     case ES_OUT_UNSET_ES:
     {
         es_out_id_t *es = va_arg( args, es_out_id_t * ), *other;
diff --git a/src/input/es_out.h b/src/input/es_out.h
index f94a7cc516..5f216905bd 100644
--- a/src/input/es_out.h
+++ b/src/input/es_out.h
@@ -50,6 +50,9 @@ enum es_out_query_private_e
     ES_OUT_SET_ES_DEFAULT_BY_ID,
     ES_OUT_GET_ES_OBJECTS_BY_ID,                    /* arg1=int id, vlc_object_t **dec, vout_thread_t **, audio_output_t ** res=can fail*/
 
+    ES_OUT_SET_ES_NEXT, /* arg1=es_category_e, arg2= vlc_es_id_t **, res=can fail */
+    ES_OUT_SET_ES_PREV, /* arg1=es_category_e, arg2= vlc_es_id_t **, res=can fail */
+
     /* Stop all selected ES and save the stopped state in a context. free the
      * context or call ES_OUT_STOP_ALL_ES */
     ES_OUT_STOP_ALL_ES,                             /* arg1=void ** */
diff --git a/src/input/es_out_timeshift.c b/src/input/es_out_timeshift.c
index 7237ca8799..5e7ffcd8e6 100644
--- a/src/input/es_out_timeshift.c
+++ b/src/input/es_out_timeshift.c
@@ -740,6 +740,8 @@ 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_NEXT:
+    case ES_OUT_SET_ES_PREV:
     case ES_OUT_RESTART_ES_BY_ID:
     case ES_OUT_SET_ES_DEFAULT_BY_ID:
     case ES_OUT_GET_ES_OBJECTS_BY_ID:
-- 
2.19.1



More information about the vlc-devel mailing list