[vlc-devel] [PATCH] input: add vlc_alternatives.h

Francois Cartegnie fcvlcdev at free.fr
Tue Aug 4 19:57:41 CEST 2015


This is a tentative to add some adaptative content selection/notifications.

We can't know in advance what streams are mapped/selected to es and
their categories. So alternatives have to be declared after first 
demuxing/es setup.

This is done though es_out_control() (for timeshift considerations when
notifying current alternative level).

There's some missing locking that should occur when updating
input thread private data. Not even sure the control() way is the correct one.
(That might explain why all updates from demux are done by polling update flags). 

Francois
---
 include/vlc_alternatives.h   | 201 +++++++++++++++++++++++++++++++++++++++++++
 include/vlc_es_out.h         |   4 +
 src/Makefile.am              |   1 +
 src/input/es_out.c           |  84 ++++++++++++++++++
 src/input/es_out_timeshift.c |  29 +++++++
 src/input/input.c            |   4 +
 src/input/input_internal.h   |   4 +
 7 files changed, 327 insertions(+)
 create mode 100644 include/vlc_alternatives.h

diff --git a/include/vlc_alternatives.h b/include/vlc_alternatives.h
new file mode 100644
index 0000000..87773cc
--- /dev/null
+++ b/include/vlc_alternatives.h
@@ -0,0 +1,201 @@
+/*****************************************************************************
+ * vlc_alternatives.h : alternatives for adaptative streaming
+ *****************************************************************************
+ * Copyright (C) 2015 VideoLAN and authors
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#ifndef VLC_ALTERNATIVES_H
+#define VLC_ALTERNATIVES_H 1
+
+#include <vlc_arrays.h>
+
+# ifdef __cplusplus
+extern "C" {
+# endif
+
+typedef int64_t alternative_id_t;
+typedef int64_t alternatives_set_id_t;
+
+typedef struct alternative_t
+{
+    char *psz_description;
+    uint64_t i_bandwidth;
+    alternative_id_t id;
+} alternative_t;
+
+typedef struct
+{
+    DECL_ARRAY( alternative_t * ) alternatives;
+    char *psz_name;
+    int i_cat;
+    alternatives_set_id_t id;
+    alternative_id_t current;
+} alternatives_set_t;
+
+
+typedef struct
+{
+    alternatives_set_t *p_list;
+    unsigned i_count;
+} alternatives_list_t;
+
+/* Alternative */
+
+static inline void alternative_Init(alternative_t * p_alt)
+{
+    p_alt->psz_description = NULL;
+    p_alt->i_bandwidth = 0;
+    p_alt->id = -1;
+}
+
+static inline alternative_t * alternative_New()
+{
+    alternative_t *p_alt = (alternative_t *) malloc(sizeof(*p_alt));
+    if(p_alt)
+        alternative_Init(p_alt);
+    return p_alt;
+}
+
+static inline bool alternatives_Append(alternatives_set_t *p_alts, alternative_t *p_alt)
+{
+    int i_alloc = p_alts->alternatives.i_alloc;
+    if(i_alloc == p_alts->alternatives.i_size)
+    {
+        if(i_alloc < 2048)
+            i_alloc = __MAX(8, i_alloc << 1);
+        else
+            i_alloc += 2048;
+        alternative_t **pp_realloc = (alternative_t **) realloc(p_alts->alternatives.p_elems,
+                                                                i_alloc * sizeof(alternative_t *));
+        if(pp_realloc)
+        {
+            p_alts->alternatives.p_elems = pp_realloc;
+            p_alts->alternatives.i_alloc = i_alloc;
+        }
+        else
+            return false;
+    }
+
+    p_alts->alternatives.p_elems[p_alts->alternatives.i_size++] = p_alt;
+
+    return true;
+}
+
+static inline void alternative_Copy(const alternative_t *p_src, alternative_t *p_dst)
+{
+    *p_dst = *p_src;
+    if(p_src->psz_description)
+        p_dst->psz_description = strdup(p_src->psz_description);
+}
+
+static inline void alternative_Clean(alternative_t *p_alt)
+{
+    free(p_alt->psz_description);
+}
+
+static inline void alternative_Free(alternative_t *p_alt)
+{
+    alternative_Clean(p_alt);
+    free(p_alt);
+}
+
+/* Alternatives list */
+
+static inline void alternatives_Init(alternatives_set_t *p_alts)
+{
+    ARRAY_INIT(p_alts->alternatives);
+    p_alts->psz_name = NULL;
+    p_alts->i_cat = 0;
+    p_alts->id = -1;
+    p_alts->current = -1;
+}
+
+static inline alternatives_set_t * alternatives_New()
+{
+    alternatives_set_t *p_alts = (alternatives_set_t *) malloc(sizeof(*p_alts));
+    if(p_alts)
+        alternatives_Init(p_alts);
+    return p_alts;
+}
+
+static inline void alternatives_Copy(const alternatives_set_t *p_srcs, alternatives_set_t *p_dsts)
+{
+    FOREACH_ARRAY(alternative_t *p_src, p_srcs->alternatives)
+        alternative_t *p_copy = alternative_New();
+        if(p_copy)
+        {
+            alternative_Copy(p_src, p_copy);
+            if(!alternatives_Append(p_dsts, p_copy))
+            {
+                alternative_Free(p_copy);
+            }
+        }
+    FOREACH_END()
+    if(p_srcs->psz_name)
+        p_dsts->psz_name = strdup(p_srcs->psz_name);
+    p_dsts->i_cat = p_srcs->i_cat;
+    p_dsts->id = p_srcs->id;
+    p_dsts->current = p_srcs->current;
+}
+
+static inline void alternatives_Clean(alternatives_set_t *p_alts)
+{
+    FOREACH_ARRAY(alternative_t *p_alt, p_alts->alternatives)
+      alternative_Free(p_alt);
+    FOREACH_END()
+    ARRAY_RESET(p_alts->alternatives);
+    free(p_alts->psz_name);
+}
+
+static inline void alternatives_Free(alternatives_set_t *p_alts)
+{
+    alternatives_Clean(p_alts);
+    free(p_alts);
+}
+
+static inline alternatives_list_t * alternatives_list_Duplicate(const alternatives_list_t *p_src)
+{
+    alternatives_list_t *p_new = (alternatives_list_t *) malloc(sizeof(*p_new));
+    if(!p_new)
+        return NULL;
+
+    p_new->i_count = p_src->i_count;
+    p_new->p_list = (alternatives_set_t *) calloc(p_src->i_count, sizeof(alternatives_set_t));
+    if(!p_new->p_list)
+    {
+        free(p_new);
+        return NULL;
+    }
+
+    for(unsigned i=0; i<p_src->i_count; i++)
+        alternatives_Copy(&p_src->p_list[i], &p_new->p_list[i]);
+
+    return p_new;
+}
+
+static inline void alternatives_list_Free(alternatives_list_t *p_list)
+{
+    for(unsigned i=0; i<p_list->i_count; i++)
+        alternatives_Clean(&p_list->p_list[i]);
+    free(p_list->p_list);
+    free(p_list);
+}
+
+# ifdef __cplusplus
+}
+# endif
+
+#endif
diff --git a/include/vlc_es_out.h b/include/vlc_es_out.h
index 58059f2..423b708 100644
--- a/include/vlc_es_out.h
+++ b/include/vlc_es_out.h
@@ -88,6 +88,10 @@ enum es_out_query_e
     ES_OUT_GET_PCR_SYSTEM, /* arg1=mtime_t *, arg2=mtime_t * res=can fail */
     ES_OUT_MODIFY_PCR_SYSTEM, /* arg1=int is_absolute, arg2=mtime_t, res=can fail */
 
+    /* Declares quality alternatives */
+    ES_OUT_SET_ALTERNATIVES, /* arg1=alternatives_list_t res=can fail */
+    ES_OUT_SET_ALTERNATIVE, /* arg1=alternatives_set_id_t, arg2=alternative_id_t res=can fail */
+
     /* First value usable for private control */
     ES_OUT_PRIVATE_START = 0x10000,
 };
diff --git a/src/Makefile.am b/src/Makefile.am
index acebc34..70771c4 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -26,6 +26,7 @@ pluginsinclude_HEADERS = \
 	../include/vlc_addons.h \
 	../include/vlc_aout.h \
 	../include/vlc_aout_volume.h \
+	../include/vlc_alternatives.h \
 	../include/vlc_arrays.h \
 	../include/vlc_atomic.h \
 	../include/vlc_avcodec.h \
diff --git a/src/input/es_out.c b/src/input/es_out.c
index 2443316..14a981d 100644
--- a/src/input/es_out.c
+++ b/src/input/es_out.c
@@ -1395,6 +1395,75 @@ static void EsOutMeta( es_out_t *p_out, const vlc_meta_t *p_meta )
     /* TODO handle sout meta ? */
 }
 
+static inline const char *GetAlternativesVar( int i_cat )
+{
+    const char *psz_var = NULL;
+    if(i_cat == VIDEO_ES)
+        psz_var = "video-alternatives";
+    else if(i_cat == AUDIO_ES)
+        psz_var = "audio-alternatives";
+    return psz_var;
+}
+
+static void EsOutAlternatives( es_out_t *p_out, const alternatives_list_t *p_list )
+{
+    es_out_sys_t    *p_sys = p_out->p_sys;
+    input_thread_t  *p_input = p_sys->p_input;
+
+    var_Change( p_input, "video-alternatives", VLC_VAR_CLEARCHOICES, NULL, NULL );
+    var_SetInteger( p_input, "video-alternatives", -1 );
+    var_Change( p_input, "audio-alternatives", VLC_VAR_CLEARCHOICES, NULL, NULL );
+    var_SetInteger( p_input, "audio-alternatives", -1 );
+
+    if(!p_list || p_list->i_count == 0)
+        return;
+
+    if( p_input->p->input.p_alternatives_list )
+        alternatives_list_Free( p_input->p->input.p_alternatives_list );
+
+    p_list = p_input->p->input.p_alternatives_list = alternatives_list_Duplicate( p_list );
+
+    for(unsigned i=0; p_list && i<p_list->i_count; i++)
+    {
+        const char *psz_var = GetAlternativesVar( p_list->p_list[i].i_cat );
+        if( psz_var )
+        {
+            vlc_value_t text, val;
+            val.i_int = -1;
+            text.psz_string = _("Auto");
+            var_Change( p_input, psz_var, VLC_VAR_ADDCHOICE, &val, &text );
+            FOREACH_ARRAY(const alternative_t *p_alt, p_list->p_list[i].alternatives)
+                text.psz_string = p_alt->psz_description;
+                val.i_int = p_alt->id;
+                var_Change( p_input, psz_var, VLC_VAR_ADDCHOICE, &val, &text );
+            FOREACH_END()
+            var_SetInteger( p_input, psz_var, p_list->p_list[i].current );
+        }
+    }
+}
+
+static void EsOutSetAlternative( es_out_t *p_out, alternatives_set_id_t group, alternative_id_t id )
+{
+    es_out_sys_t    *p_sys = p_out->p_sys;
+    input_thread_t  *p_input = p_sys->p_input;
+
+    const alternatives_list_t *p_list = p_input->p->input.p_alternatives_list;
+    if(!p_list)
+        return;
+
+    for(unsigned i=0; p_list->i_count; i++)
+    {
+        if(p_list->p_list[i].id == group)
+        {
+            p_list->p_list[i].current = id;
+            const char *psz_var = GetAlternativesVar( p_list->p_list[i].i_cat );
+            if(psz_var)
+                var_SetInteger( p_input, psz_var, p_list->p_list[i].current );
+            break;
+        }
+    }
+}
+
 /* EsOutAdd:
  *  Add an es_out
  */
@@ -2702,6 +2771,21 @@ static int EsOutControlLocked( es_out_t *out, int i_query, va_list args )
         return VLC_SUCCESS;
     }
 
+    case ES_OUT_SET_ALTERNATIVES:
+    {
+        const alternatives_list_t *p_list = va_arg( args, alternatives_list_t * );
+        EsOutAlternatives( out, p_list );
+        return VLC_SUCCESS;
+    }
+
+    case ES_OUT_SET_ALTERNATIVE:
+    {
+        alternatives_set_id_t group_id = va_arg( args, alternatives_set_id_t );
+        alternative_id_t alt_id = va_arg( args, alternative_id_t );
+        EsOutSetAlternative( out, group_id, alt_id );
+        return VLC_SUCCESS;
+    }
+
     default:
         msg_Err( p_sys->p_input, "unknown query in es_out_Control" );
         return VLC_EGENERIC;
diff --git a/src/input/es_out_timeshift.c b/src/input/es_out_timeshift.c
index d95a3aa..bde2cbe 100644
--- a/src/input/es_out_timeshift.c
+++ b/src/input/es_out_timeshift.c
@@ -134,6 +134,10 @@ typedef struct attribute_packed
             mtime_t i_pts_jitter;
             int     i_cr_average;
         } jitter;
+        struct
+        {
+            alternatives_list_t *p_list;
+        } alternatives;
     } u;
 } ts_cmd_control_t;
 
@@ -596,6 +600,8 @@ static int ControlLocked( es_out_t *p_out, int i_query, va_list args )
     case ES_OUT_SET_TIMES:
     case ES_OUT_SET_JITTER:
     case ES_OUT_SET_EOS:
+    case ES_OUT_SET_ALTERNATIVES:
+    case ES_OUT_SET_ALTERNATIVE:
     {
         ts_cmd_t cmd;
         if( CmdInitControl( &cmd, i_query, args, p_sys->b_delayed ) )
@@ -1434,6 +1440,18 @@ static int CmdInitControl( ts_cmd_t *p_cmd, int i_query, va_list args, bool b_co
         break;
     }
 
+        // TODO
+    case ES_OUT_SET_ALTERNATIVE:
+        return VLC_EGENERIC;
+    case ES_OUT_SET_ALTERNATIVES:
+    {
+        const alternatives_list_t *p_list = (alternatives_list_t *)va_arg( args, alternatives_list_t * );
+        p_cmd->u.control.u.alternatives.p_list = alternatives_list_Duplicate( p_list );
+        if( !p_cmd->u.control.u.alternatives.p_list )
+            return VLC_EGENERIC;
+        break;
+    }
+
     default:
         vlc_assert_unreachable();
         return VLC_EGENERIC;
@@ -1503,6 +1521,13 @@ static int CmdExecuteControl( es_out_t *p_out, ts_cmd_t *p_cmd )
                                                p_cmd->u.control.u.jitter.i_pts_jitter,
                                                p_cmd->u.control.u.jitter.i_cr_average );
 
+        // TODO
+    case ES_OUT_SET_ALTERNATIVE:
+        return VLC_EGENERIC;
+
+    case ES_OUT_SET_ALTERNATIVES:
+        return es_out_Control( p_out, i_query, p_cmd->u.control.u.alternatives.p_list );
+
     default:
         vlc_assert_unreachable();
         return VLC_EGENERIC;
@@ -1512,6 +1537,10 @@ static void CmdCleanControl( ts_cmd_t *p_cmd )
 {
     switch( p_cmd->u.control.i_query )
     {
+    case ES_OUT_SET_ALTERNATIVES:
+        if( p_cmd->u.control.u.alternatives.p_list )
+            alternatives_list_Free( p_cmd->u.control.u.alternatives.p_list );
+        break;
     case ES_OUT_SET_GROUP_META:
     case ES_OUT_SET_META:
         if( p_cmd->u.control.u.int_meta.p_meta )
diff --git a/src/input/input.c b/src/input/input.c
index a822df7..d67abd4 100644
--- a/src/input/input.c
+++ b/src/input/input.c
@@ -365,6 +365,7 @@ static input_thread_t *Create( vlc_object_t *p_parent, input_item_t *p_item,
     p_input->p->input.i_title  = 0;
     p_input->p->input.title    = NULL;
     p_input->p->input.i_title_offset = p_input->p->input.i_seekpoint_offset = 0;
+    p_input->p->input.p_alternatives_list = NULL;
     p_input->p->input.b_can_pace_control = true;
     p_input->p->input.b_can_rate_control = true;
     p_input->p->input.b_rescale_ts = true;
@@ -2460,6 +2461,9 @@ static void InputSourceClean( input_source_t *in )
             vlc_input_title_Delete( in->title[i] );
         TAB_CLEAN( in->i_title, in->title );
     }
+
+    if( in->p_alternatives_list )
+        alternatives_list_Free( in->p_alternatives_list );
 }
 
 /*****************************************************************************
diff --git a/src/input/input_internal.h b/src/input/input_internal.h
index cf27ff3..c1cdd57 100644
--- a/src/input/input_internal.h
+++ b/src/input/input_internal.h
@@ -27,6 +27,7 @@
 #include <vlc_access.h>
 #include <vlc_demux.h>
 #include <vlc_input.h>
+#include <vlc_alternatives.h>
 #include <libvlc.h>
 #include "input_interface.h"
 #include "misc/interrupt.h"
@@ -55,6 +56,9 @@ typedef struct
     int i_seekpoint_start;
     int i_seekpoint_end;
 
+    /* Alternatives sets */
+    alternatives_list_t *p_alternatives_list;
+
     /* Properties */
     bool b_can_pause;
     bool b_can_pace_control;
-- 
2.4.3




More information about the vlc-devel mailing list