[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